| // Copyright 2017 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "dawn/native/opengl/TextureGL.h" | |
| #include <limits> | |
| #include <utility> | |
| #include "dawn/common/Assert.h" | |
| #include "dawn/common/Constants.h" | |
| #include "dawn/common/Math.h" | |
| #include "dawn/native/EnumMaskIterator.h" | |
| #include "dawn/native/opengl/BufferGL.h" | |
| #include "dawn/native/opengl/CommandBufferGL.h" | |
| #include "dawn/native/opengl/DeviceGL.h" | |
| #include "dawn/native/opengl/UtilsGL.h" | |
| namespace dawn::native::opengl { | |
| namespace { | |
| GLenum TargetForTexture(const TextureDescriptor* descriptor) { | |
| switch (descriptor->dimension) { | |
| case wgpu::TextureDimension::e1D: | |
| case wgpu::TextureDimension::e2D: | |
| if (descriptor->size.depthOrArrayLayers > 1) { | |
| DAWN_ASSERT(descriptor->sampleCount == 1); | |
| return GL_TEXTURE_2D_ARRAY; | |
| } else { | |
| if (descriptor->sampleCount > 1) { | |
| return GL_TEXTURE_2D_MULTISAMPLE; | |
| } else { | |
| return GL_TEXTURE_2D; | |
| } | |
| } | |
| case wgpu::TextureDimension::e3D: | |
| DAWN_ASSERT(descriptor->sampleCount == 1); | |
| return GL_TEXTURE_3D; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension, | |
| uint32_t arrayLayerCount, | |
| uint32_t sampleCount) { | |
| switch (dimension) { | |
| case wgpu::TextureViewDimension::e1D: | |
| case wgpu::TextureViewDimension::e2D: | |
| return (sampleCount > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; | |
| case wgpu::TextureViewDimension::e2DArray: | |
| if (sampleCount > 1) { | |
| DAWN_ASSERT(arrayLayerCount == 1); | |
| return GL_TEXTURE_2D_MULTISAMPLE; | |
| } | |
| DAWN_ASSERT(sampleCount == 1); | |
| return GL_TEXTURE_2D_ARRAY; | |
| case wgpu::TextureViewDimension::Cube: | |
| DAWN_ASSERT(sampleCount == 1); | |
| DAWN_ASSERT(arrayLayerCount == 6); | |
| return GL_TEXTURE_CUBE_MAP; | |
| case wgpu::TextureViewDimension::CubeArray: | |
| DAWN_ASSERT(sampleCount == 1); | |
| DAWN_ASSERT(arrayLayerCount % 6 == 0); | |
| return GL_TEXTURE_CUBE_MAP_ARRAY; | |
| case wgpu::TextureViewDimension::e3D: | |
| return GL_TEXTURE_3D; | |
| case wgpu::TextureViewDimension::Undefined: | |
| break; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| bool RequiresCreatingNewTextureView(const TextureBase* texture, | |
| const TextureViewDescriptor* textureViewDescriptor) { | |
| constexpr wgpu::TextureUsage kShaderUsageNeedsView = | |
| wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding; | |
| constexpr wgpu::TextureUsage kUsageNeedsView = | |
| kShaderUsageNeedsView | wgpu::TextureUsage::RenderAttachment; | |
| if ((texture->GetInternalUsage() & kUsageNeedsView) == 0) { | |
| return false; | |
| } | |
| if (texture->GetFormat().format != textureViewDescriptor->format && | |
| !texture->GetFormat().HasDepthOrStencil()) { | |
| // Color format reinterpretation required. Note: Depth/stencil formats don't support | |
| // reinterpretation. | |
| return true; | |
| } | |
| // Reinterpretation not required. Now, we only need a new view if the view dimension or | |
| // set of subresources for the shader is different from the base texture. | |
| if ((texture->GetInternalUsage() & kShaderUsageNeedsView) == 0) { | |
| return false; | |
| } | |
| if (texture->GetArrayLayers() != textureViewDescriptor->arrayLayerCount || | |
| (texture->GetArrayLayers() == 1 && texture->GetDimension() == wgpu::TextureDimension::e2D && | |
| textureViewDescriptor->dimension == wgpu::TextureViewDimension::e2DArray)) { | |
| // If the view has a different number of array layers, we need a new view. | |
| // And, if the original texture is a 2D texture with one array layer, we need a new | |
| // view to view it as a 2D array texture. | |
| return true; | |
| } | |
| if (texture->GetNumMipLevels() != textureViewDescriptor->mipLevelCount) { | |
| return true; | |
| } | |
| if (ToBackend(texture)->GetGLFormat().format == GL_DEPTH_STENCIL && | |
| (texture->GetUsage() & wgpu::TextureUsage::TextureBinding) != 0 && | |
| textureViewDescriptor->aspect == wgpu::TextureAspect::StencilOnly) { | |
| // We need a separate view for one of the depth or stencil planes | |
| // because each glTextureView needs it's own handle to set | |
| // GL_DEPTH_STENCIL_TEXTURE_MODE. Choose the stencil aspect for the | |
| // extra handle since it is likely sampled less often. | |
| return true; | |
| } | |
| switch (textureViewDescriptor->dimension) { | |
| case wgpu::TextureViewDimension::Cube: | |
| case wgpu::TextureViewDimension::CubeArray: | |
| return true; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| void AllocateTexture(const OpenGLFunctions& gl, | |
| GLenum target, | |
| GLsizei samples, | |
| GLuint levels, | |
| GLenum internalFormat, | |
| const Extent3D& size) { | |
| // glTextureView() requires the value of GL_TEXTURE_IMMUTABLE_FORMAT for origtexture to | |
| // be GL_TRUE, so the storage of the texture must be allocated with glTexStorage*D. | |
| // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTextureView.xhtml | |
| switch (target) { | |
| case GL_TEXTURE_2D_ARRAY: | |
| case GL_TEXTURE_3D: | |
| gl.TexStorage3D(target, levels, internalFormat, size.width, size.height, | |
| size.depthOrArrayLayers); | |
| break; | |
| case GL_TEXTURE_2D: | |
| case GL_TEXTURE_CUBE_MAP: | |
| gl.TexStorage2D(target, levels, internalFormat, size.width, size.height); | |
| break; | |
| case GL_TEXTURE_2D_MULTISAMPLE: | |
| gl.TexStorage2DMultisample(target, samples, internalFormat, size.width, size.height, | |
| true); | |
| break; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| } | |
| } | |
| } // namespace | |
| // Texture | |
| // static | |
| ResultOrError<Ref<Texture>> Texture::Create(Device* device, const TextureDescriptor* descriptor) { | |
| Ref<Texture> texture = AcquireRef(new Texture(device, descriptor)); | |
| if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { | |
| DAWN_TRY( | |
| texture->ClearTexture(texture->GetAllSubresources(), TextureBase::ClearValue::NonZero)); | |
| } | |
| return std::move(texture); | |
| } | |
| Texture::Texture(Device* device, const TextureDescriptor* descriptor) | |
| : Texture(device, descriptor, 0) { | |
| const OpenGLFunctions& gl = device->GetGL(); | |
| gl.GenTextures(1, &mHandle); | |
| mOwnsHandle = true; | |
| uint32_t levels = GetNumMipLevels(); | |
| const GLFormat& glFormat = GetGLFormat(); | |
| gl.BindTexture(mTarget, mHandle); | |
| AllocateTexture(gl, mTarget, GetSampleCount(), levels, glFormat.internalFormat, GetBaseSize()); | |
| // The texture is not complete if it uses mipmapping and not all levels up to | |
| // MAX_LEVEL have been defined. | |
| gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1); | |
| } | |
| void Texture::Touch() { | |
| mGenID++; | |
| } | |
| uint32_t Texture::GetGenID() const { | |
| return mGenID; | |
| } | |
| Texture::Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle) | |
| : TextureBase(device, descriptor), mHandle(handle) { | |
| mTarget = TargetForTexture(descriptor); | |
| } | |
| Texture::~Texture() {} | |
| void Texture::DestroyImpl() { | |
| TextureBase::DestroyImpl(); | |
| if (mOwnsHandle) { | |
| const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL(); | |
| gl.DeleteTextures(1, &mHandle); | |
| mHandle = 0; | |
| } | |
| } | |
| GLuint Texture::GetHandle() const { | |
| return mHandle; | |
| } | |
| GLenum Texture::GetGLTarget() const { | |
| return mTarget; | |
| } | |
| const GLFormat& Texture::GetGLFormat() const { | |
| return ToBackend(GetDevice())->GetGLFormat(GetFormat()); | |
| } | |
| MaybeError Texture::ClearTexture(const SubresourceRange& range, | |
| TextureBase::ClearValue clearValue) { | |
| // TODO(crbug.com/dawn/850): initialize the textures with compressed formats. | |
| if (GetFormat().isCompressed) { | |
| return {}; | |
| } | |
| Device* device = ToBackend(GetDevice()); | |
| const OpenGLFunctions& gl = device->GetGL(); | |
| uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; | |
| float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f; | |
| if (GetFormat().isRenderable) { | |
| if (range.aspects & (Aspect::Depth | Aspect::Stencil)) { | |
| GLfloat depth = fClearColor; | |
| GLint stencil = clearColor; | |
| if (range.aspects & Aspect::Depth) { | |
| gl.DepthMask(GL_TRUE); | |
| } | |
| if (range.aspects & Aspect::Stencil) { | |
| gl.StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format)); | |
| } | |
| auto DoClear = [&](Aspect aspects) { | |
| if (aspects == (Aspect::Depth | Aspect::Stencil)) { | |
| gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil); | |
| } else if (aspects == Aspect::Depth) { | |
| gl.ClearBufferfv(GL_DEPTH, 0, &depth); | |
| } else if (aspects == Aspect::Stencil) { | |
| gl.ClearBufferiv(GL_STENCIL, 0, &stencil); | |
| } else { | |
| DAWN_UNREACHABLE(); | |
| } | |
| }; | |
| GLuint framebuffer = 0; | |
| gl.GenFramebuffers(1, &framebuffer); | |
| gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); | |
| gl.Disable(GL_SCISSOR_TEST); | |
| GLenum attachment; | |
| if (range.aspects == (Aspect::Depth | Aspect::Stencil)) { | |
| attachment = GL_DEPTH_STENCIL_ATTACHMENT; | |
| } else if (range.aspects == Aspect::Depth) { | |
| attachment = GL_DEPTH_ATTACHMENT; | |
| } else if (range.aspects == Aspect::Stencil) { | |
| attachment = GL_STENCIL_ATTACHMENT; | |
| } else { | |
| DAWN_UNREACHABLE(); | |
| } | |
| for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; | |
| ++level) { | |
| switch (GetDimension()) { | |
| case wgpu::TextureDimension::e1D: | |
| case wgpu::TextureDimension::e2D: | |
| if (GetArrayLayers() == 1) { | |
| Aspect aspectsToClear = Aspect::None; | |
| for (Aspect aspect : IterateEnumMask(range.aspects)) { | |
| if (clearValue == TextureBase::ClearValue::Zero && | |
| IsSubresourceContentInitialized( | |
| SubresourceRange::SingleMipAndLayer(level, 0, aspect))) { | |
| // Skip lazy clears if already initialized. | |
| continue; | |
| } | |
| aspectsToClear |= aspect; | |
| } | |
| if (aspectsToClear == Aspect::None) { | |
| continue; | |
| } | |
| gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GetGLTarget(), | |
| GetHandle(), static_cast<GLint>(level)); | |
| DoClear(aspectsToClear); | |
| } else { | |
| for (uint32_t layer = range.baseArrayLayer; | |
| layer < range.baseArrayLayer + range.layerCount; ++layer) { | |
| Aspect aspectsToClear = Aspect::None; | |
| for (Aspect aspect : IterateEnumMask(range.aspects)) { | |
| if (clearValue == TextureBase::ClearValue::Zero && | |
| IsSubresourceContentInitialized( | |
| SubresourceRange::SingleMipAndLayer(level, layer, | |
| aspect))) { | |
| // Skip lazy clears if already initialized. | |
| continue; | |
| } | |
| aspectsToClear |= aspect; | |
| } | |
| if (aspectsToClear == Aspect::None) { | |
| continue; | |
| } | |
| gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, | |
| GetHandle(), static_cast<GLint>(level), | |
| static_cast<GLint>(layer)); | |
| DoClear(aspectsToClear); | |
| } | |
| } | |
| break; | |
| case wgpu::TextureDimension::e3D: | |
| DAWN_UNREACHABLE(); | |
| } | |
| } | |
| gl.Enable(GL_SCISSOR_TEST); | |
| gl.DeleteFramebuffers(1, &framebuffer); | |
| } else { | |
| DAWN_ASSERT(range.aspects == Aspect::Color); | |
| // For gl.ClearBufferiv/uiv calls | |
| constexpr std::array<GLuint, 4> kClearColorDataUint0 = {0u, 0u, 0u, 0u}; | |
| constexpr std::array<GLuint, 4> kClearColorDataUint1 = {1u, 1u, 1u, 1u}; | |
| std::array<GLuint, 4> clearColorData; | |
| clearColorData.fill((clearValue == TextureBase::ClearValue::Zero) ? 0u : 1u); | |
| // For gl.ClearBufferfv calls | |
| constexpr std::array<GLfloat, 4> kClearColorDataFloat0 = {0.f, 0.f, 0.f, 0.f}; | |
| constexpr std::array<GLfloat, 4> kClearColorDataFloat1 = {1.f, 1.f, 1.f, 1.f}; | |
| std::array<GLfloat, 4> fClearColorData; | |
| fClearColorData.fill((clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f); | |
| static constexpr uint32_t MAX_TEXEL_SIZE = 16; | |
| const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(Aspect::Color).block; | |
| DAWN_ASSERT(blockInfo.byteSize <= MAX_TEXEL_SIZE); | |
| // For gl.ClearTexSubImage calls | |
| constexpr std::array<GLbyte, MAX_TEXEL_SIZE> kClearColorDataBytes0 = { | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |
| constexpr std::array<GLbyte, MAX_TEXEL_SIZE> kClearColorDataBytes255 = { | |
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; | |
| TextureComponentType baseType = GetFormat().GetAspectInfo(Aspect::Color).baseType; | |
| const GLFormat& glFormat = GetGLFormat(); | |
| for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; | |
| ++level) { | |
| Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color); | |
| for (uint32_t layer = range.baseArrayLayer; | |
| layer < range.baseArrayLayer + range.layerCount; ++layer) { | |
| if (clearValue == TextureBase::ClearValue::Zero && | |
| IsSubresourceContentInitialized( | |
| SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) { | |
| // Skip lazy clears if already initialized. | |
| continue; | |
| } | |
| if (gl.IsAtLeastGL(4, 4)) { | |
| gl.ClearTexSubImage(mHandle, static_cast<GLint>(level), 0, 0, | |
| static_cast<GLint>(layer), mipSize.width, | |
| mipSize.height, mipSize.depthOrArrayLayers, | |
| glFormat.format, glFormat.type, | |
| clearValue == TextureBase::ClearValue::Zero | |
| ? kClearColorDataBytes0.data() | |
| : kClearColorDataBytes255.data()); | |
| continue; | |
| } | |
| GLuint framebuffer = 0; | |
| gl.GenFramebuffers(1, &framebuffer); | |
| gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); | |
| GLenum attachment = GL_COLOR_ATTACHMENT0; | |
| gl.DrawBuffers(1, &attachment); | |
| gl.Disable(GL_SCISSOR_TEST); | |
| gl.ColorMask(true, true, true, true); | |
| auto DoClear = [&] { | |
| switch (baseType) { | |
| case TextureComponentType::Float: { | |
| gl.ClearBufferfv(GL_COLOR, 0, | |
| clearValue == TextureBase::ClearValue::Zero | |
| ? kClearColorDataFloat0.data() | |
| : kClearColorDataFloat1.data()); | |
| break; | |
| } | |
| case TextureComponentType::Uint: { | |
| gl.ClearBufferuiv(GL_COLOR, 0, | |
| clearValue == TextureBase::ClearValue::Zero | |
| ? kClearColorDataUint0.data() | |
| : kClearColorDataUint1.data()); | |
| break; | |
| } | |
| case TextureComponentType::Sint: { | |
| gl.ClearBufferiv(GL_COLOR, 0, | |
| reinterpret_cast<const GLint*>( | |
| clearValue == TextureBase::ClearValue::Zero | |
| ? kClearColorDataUint0.data() | |
| : kClearColorDataUint1.data())); | |
| break; | |
| } | |
| } | |
| }; | |
| if (GetArrayLayers() == 1) { | |
| switch (GetDimension()) { | |
| case wgpu::TextureDimension::e1D: | |
| case wgpu::TextureDimension::e2D: | |
| gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, | |
| GetGLTarget(), GetHandle(), level); | |
| DoClear(); | |
| break; | |
| case wgpu::TextureDimension::e3D: | |
| uint32_t depth = | |
| GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color) | |
| .depthOrArrayLayers; | |
| for (GLint z = 0; z < static_cast<GLint>(depth); ++z) { | |
| gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, | |
| GetHandle(), level, z); | |
| DoClear(); | |
| } | |
| break; | |
| } | |
| } else { | |
| DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D); | |
| gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, GetHandle(), | |
| level, layer); | |
| DoClear(); | |
| } | |
| gl.Enable(GL_SCISSOR_TEST); | |
| gl.DeleteFramebuffers(1, &framebuffer); | |
| gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | |
| } | |
| } | |
| } | |
| } else { | |
| DAWN_ASSERT(range.aspects == Aspect::Color); | |
| // create temp buffer with clear color to copy to the texture image | |
| const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(Aspect::Color).block; | |
| DAWN_ASSERT(kTextureBytesPerRowAlignment % blockInfo.byteSize == 0); | |
| Extent3D largestMipSize = | |
| GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel, Aspect::Color); | |
| uint32_t bytesPerRow = | |
| Align((largestMipSize.width / blockInfo.width) * blockInfo.byteSize, 4); | |
| // Make sure that we are not rounding | |
| DAWN_ASSERT(bytesPerRow % blockInfo.byteSize == 0); | |
| DAWN_ASSERT(largestMipSize.height % blockInfo.height == 0); | |
| uint64_t bufferSize64 = static_cast<uint64_t>(bytesPerRow) * | |
| (largestMipSize.height / blockInfo.height) * | |
| largestMipSize.depthOrArrayLayers; | |
| if (bufferSize64 > std::numeric_limits<size_t>::max()) { | |
| return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); | |
| } | |
| size_t bufferSize = static_cast<size_t>(bufferSize64); | |
| dawn::native::BufferDescriptor descriptor = {}; | |
| descriptor.mappedAtCreation = true; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc; | |
| descriptor.size = bufferSize; | |
| // We don't count the lazy clear of srcBuffer because it is an internal buffer. | |
| // TODO(natlee@microsoft.com): use Dynamic Uploader here for temp buffer | |
| Ref<Buffer> srcBuffer; | |
| DAWN_TRY_ASSIGN(srcBuffer, Buffer::CreateInternalBuffer(device, &descriptor, false)); | |
| // Fill the buffer with clear color | |
| memset(srcBuffer->GetMappedRange(0, bufferSize), clearColor, bufferSize); | |
| DAWN_TRY(srcBuffer->Unmap()); | |
| gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, srcBuffer->GetHandle()); | |
| for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; | |
| ++level) { | |
| TextureCopy textureCopy; | |
| textureCopy.texture = this; | |
| textureCopy.mipLevel = level; | |
| textureCopy.origin = {}; | |
| textureCopy.aspect = Aspect::Color; | |
| TextureDataLayout dataLayout; | |
| dataLayout.offset = 0; | |
| dataLayout.bytesPerRow = bytesPerRow; | |
| dataLayout.rowsPerImage = largestMipSize.height; | |
| Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color); | |
| for (uint32_t layer = range.baseArrayLayer; | |
| layer < range.baseArrayLayer + range.layerCount; ++layer) { | |
| if (clearValue == TextureBase::ClearValue::Zero && | |
| IsSubresourceContentInitialized( | |
| SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) { | |
| // Skip lazy clears if already initialized. | |
| continue; | |
| } | |
| textureCopy.origin.z = layer; | |
| DoTexSubImage(gl, textureCopy, 0, dataLayout, mipSize); | |
| } | |
| } | |
| gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | |
| } | |
| if (clearValue == TextureBase::ClearValue::Zero) { | |
| SetIsSubresourceContentInitialized(true, range); | |
| device->IncrementLazyClearCountForTesting(); | |
| } | |
| Touch(); | |
| return {}; | |
| } | |
| MaybeError Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) { | |
| if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { | |
| return {}; | |
| } | |
| if (!IsSubresourceContentInitialized(range)) { | |
| DAWN_TRY(ClearTexture(range, TextureBase::ClearValue::Zero)); | |
| } | |
| return {}; | |
| } | |
| // TextureView | |
| TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) | |
| : TextureViewBase(texture, descriptor), mOwnsHandle(false) { | |
| mTarget = TargetForTextureViewDimension(descriptor->dimension, descriptor->arrayLayerCount, | |
| texture->GetSampleCount()); | |
| // Texture could be destroyed by the time we make a view. | |
| if (GetTexture()->IsDestroyed()) { | |
| return; | |
| } | |
| if (!RequiresCreatingNewTextureView(texture, descriptor)) { | |
| mHandle = ToBackend(texture)->GetHandle(); | |
| } else { | |
| const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL(); | |
| if (gl.IsAtLeastGL(4, 3)) { | |
| gl.GenTextures(1, &mHandle); | |
| const Texture* textureGL = ToBackend(texture); | |
| gl.TextureView(mHandle, mTarget, textureGL->GetHandle(), GetInternalFormat(), | |
| descriptor->baseMipLevel, descriptor->mipLevelCount, | |
| descriptor->baseArrayLayer, descriptor->arrayLayerCount); | |
| mOwnsHandle = true; | |
| } else { | |
| // Simulate glTextureView() with texture-to-texture copies. | |
| mUseCopy = true; | |
| mHandle = 0; | |
| } | |
| } | |
| } | |
| TextureView::~TextureView() {} | |
| void TextureView::DestroyImpl() { | |
| TextureViewBase::DestroyImpl(); | |
| if (mOwnsHandle) { | |
| const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL(); | |
| gl.DeleteTextures(1, &mHandle); | |
| } | |
| } | |
| GLuint TextureView::GetHandle() const { | |
| DAWN_ASSERT(mHandle != 0); | |
| return mHandle; | |
| } | |
| GLenum TextureView::GetGLTarget() const { | |
| return mTarget; | |
| } | |
| void TextureView::BindToFramebuffer(GLenum target, GLenum attachment) { | |
| const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL(); | |
| // Use the base texture where possible to minimize the amount of copying required on GLES. | |
| bool useOwnView = GetFormat().format != GetTexture()->GetFormat().format && | |
| !GetTexture()->GetFormat().HasDepthOrStencil(); | |
| GLuint handle, textarget, mipLevel, arrayLayer; | |
| if (useOwnView) { | |
| // Use our own texture handle and target which points to a subset of the texture's | |
| // subresources. | |
| handle = GetHandle(); | |
| textarget = GetGLTarget(); | |
| mipLevel = 0; | |
| arrayLayer = 0; | |
| } else { | |
| // Use the texture's handle and target, with the view's base mip level and base array | |
| handle = ToBackend(GetTexture())->GetHandle(); | |
| textarget = ToBackend(GetTexture())->GetGLTarget(); | |
| mipLevel = GetBaseMipLevel(); | |
| arrayLayer = GetBaseArrayLayer(); | |
| } | |
| DAWN_ASSERT(handle != 0); | |
| if (textarget == GL_TEXTURE_2D_ARRAY || textarget == GL_TEXTURE_3D) { | |
| gl.FramebufferTextureLayer(target, attachment, handle, mipLevel, arrayLayer); | |
| } else { | |
| gl.FramebufferTexture2D(target, attachment, textarget, handle, mipLevel); | |
| } | |
| } | |
| void TextureView::CopyIfNeeded() { | |
| if (!mUseCopy) { | |
| return; | |
| } | |
| const Texture* texture = ToBackend(GetTexture()); | |
| if (mGenID == texture->GetGenID()) { | |
| return; | |
| } | |
| Device* device = ToBackend(GetDevice()); | |
| const OpenGLFunctions& gl = device->GetGL(); | |
| uint32_t srcLevel = GetBaseMipLevel(); | |
| uint32_t numLevels = GetLevelCount(); | |
| uint32_t width = GetSingleSubresourceVirtualSize().width; | |
| uint32_t height = GetSingleSubresourceVirtualSize().height; | |
| Extent3D size{width, height, GetLayerCount()}; | |
| if (mHandle == 0) { | |
| gl.GenTextures(1, &mHandle); | |
| gl.BindTexture(mTarget, mHandle); | |
| AllocateTexture(gl, mTarget, texture->GetSampleCount(), numLevels, GetInternalFormat(), | |
| size); | |
| mOwnsHandle = true; | |
| } | |
| Origin3D src{0, 0, GetBaseArrayLayer()}; | |
| Origin3D dst{0, 0, 0}; | |
| for (GLuint level = 0; level < numLevels; ++level) { | |
| CopyImageSubData(gl, GetAspects(), texture->GetHandle(), texture->GetGLTarget(), | |
| srcLevel + level, src, mHandle, mTarget, level, dst, size); | |
| } | |
| mGenID = texture->GetGenID(); | |
| } | |
| GLenum TextureView::GetInternalFormat() const { | |
| // Depth/stencil don't support reinterpretation, and the aspect is specified at | |
| // bind time. In that case, we use the base texture format. | |
| const Format& format = | |
| GetFormat().HasDepthOrStencil() ? GetTexture()->GetFormat() : GetFormat(); | |
| const GLFormat& glFormat = ToBackend(GetDevice())->GetGLFormat(format); | |
| return glFormat.internalFormat; | |
| } | |
| } // namespace dawn::native::opengl |