| // 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 <algorithm> |
| #include <limits> |
| #include <utility> |
| |
| #include "dawn/common/Assert.h" |
| #include "dawn/common/Constants.h" |
| #include "dawn/common/Math.h" |
| #include "dawn/native/ChainUtils.h" |
| #include "dawn/native/Device.h" |
| #include "dawn/native/EnumMaskIterator.h" |
| #include "dawn/native/Queue.h" |
| #include "dawn/native/opengl/BufferGL.h" |
| #include "dawn/native/opengl/CommandBufferGL.h" |
| #include "dawn/native/opengl/DeviceGL.h" |
| #include "dawn/native/opengl/SharedFenceGL.h" |
| #include "dawn/native/opengl/SharedTextureMemoryGL.h" |
| #include "dawn/native/opengl/UtilsGL.h" |
| |
| namespace dawn::native::opengl { |
| |
| namespace { |
| |
| GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension, 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) { |
| return GL_TEXTURE_2D_MULTISAMPLE; |
| } |
| DAWN_ASSERT(sampleCount == 1); |
| return GL_TEXTURE_2D_ARRAY; |
| case wgpu::TextureViewDimension::Cube: |
| DAWN_ASSERT(sampleCount == 1); |
| return GL_TEXTURE_CUBE_MAP; |
| case wgpu::TextureViewDimension::CubeArray: |
| DAWN_ASSERT(sampleCount == 1); |
| return GL_TEXTURE_CUBE_MAP_ARRAY; |
| case wgpu::TextureViewDimension::e3D: |
| DAWN_ASSERT(sampleCount == 1); |
| return GL_TEXTURE_3D; |
| |
| case wgpu::TextureViewDimension::Undefined: |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| |
| // Note this only applies to Desktop OpenGL (texture views don't exist in GLES). |
| // In compatibility mode, validation should mean we never need texture views. |
| bool RequiresCreatingNewTextureView( |
| const TextureBase* texture, |
| const UnpackedPtr<TextureViewDescriptor>& textureViewDescriptor) { |
| if (ToBackend(texture->GetDevice())->IsCompatibilityMode()) { |
| return false; |
| } |
| |
| constexpr wgpu::TextureUsage kShaderUsageNeedsView = |
| wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding; |
| constexpr wgpu::TextureUsage kUsageNeedsView = |
| kShaderUsageNeedsView | wgpu::TextureUsage::RenderAttachment; |
| if (!(texture->GetInternalUsage() & kUsageNeedsView)) { |
| 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)) { |
| 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 (ToBackend(texture)->GetGLFormat().format == GL_DEPTH_STENCIL && |
| (texture->GetUsage() & wgpu::TextureUsage::TextureBinding) && |
| 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; |
| } |
| |
| // Note: For formats with <4 channels, this could be refined to consider that some channels are |
| // nonexistent. We don't bother to optimize that, because such swizzles are not actually useful. |
| // (Also, this code is only reached on Desktop GL anyway.) |
| if (auto* swizzleDesc = textureViewDescriptor.Get<TextureComponentSwizzleDescriptor>()) { |
| auto swizzle = swizzleDesc->swizzle.WithTrivialFrontendDefaults(); |
| if (*ToCppAPI(&swizzle) != kRGBASwizzle) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| MaybeError AllocateTexture(const OpenGLFunctions& gl, |
| GLenum target, |
| GLsizei samples, |
| GLuint levels, |
| const GLFormat& format, |
| const Extent3D& size) { |
| if (format.isSupportedForTextureStorage || target == GL_TEXTURE_2D_MULTISAMPLE) { |
| // 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 |
| |
| // There is no fallback for multisampled textures. They must be allocated with |
| // glTexStorage2DMultisample |
| switch (target) { |
| case GL_TEXTURE_2D_ARRAY: |
| case GL_TEXTURE_CUBE_MAP_ARRAY: |
| case GL_TEXTURE_3D: |
| DAWN_GL_TRY_ALWAYS_CHECK( |
| gl, TexStorage3D(target, levels, format.internalFormat, size.width, size.height, |
| size.depthOrArrayLayers)); |
| break; |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_CUBE_MAP: |
| DAWN_GL_TRY_ALWAYS_CHECK(gl, TexStorage2D(target, levels, format.internalFormat, |
| size.width, size.height)); |
| break; |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| DAWN_GL_TRY_ALWAYS_CHECK( |
| gl, TexStorage2DMultisample(target, samples, format.internalFormat, size.width, |
| size.height, true)); |
| break; |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } else { |
| // Allocate the texture using multiple glTexImage. This should only happen in compat and the |
| // resulting texture will not be usable with glTextureView |
| |
| // When using glTexImage, make sure there is no unpack buffer bound or data would be copied |
| // from whatever buffer happens to be bound. |
| DAWN_GL_TRY_ALWAYS_CHECK(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)); |
| |
| // Array texture formats have the same depth for all mip levels. |
| bool constantSizeForAllLevels = |
| target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_CUBE_MAP_ARRAY; |
| |
| for (GLuint level = 0; level < levels; level++) { |
| Extent3D levelSize{ |
| std::max(size.width >> level, 1u), std::max(size.height >> level, 1u), |
| constantSizeForAllLevels ? size.depthOrArrayLayers |
| : std::max(size.depthOrArrayLayers >> level, 1u)}; |
| |
| switch (target) { |
| case GL_TEXTURE_2D_ARRAY: |
| case GL_TEXTURE_CUBE_MAP_ARRAY: |
| case GL_TEXTURE_3D: |
| DAWN_GL_TRY_ALWAYS_CHECK( |
| gl, TexImage3D(target, level, format.format, levelSize.width, |
| levelSize.height, levelSize.depthOrArrayLayers, 0, |
| format.format, format.type, nullptr)); |
| break; |
| case GL_TEXTURE_2D: |
| DAWN_GL_TRY_ALWAYS_CHECK( |
| gl, TexImage2D(target, level, format.format, levelSize.width, |
| levelSize.height, 0, format.format, format.type, nullptr)); |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| for (size_t faceIdx = 0; faceIdx < 6; faceIdx++) { |
| GLenum faceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIdx; |
| DAWN_GL_TRY_ALWAYS_CHECK( |
| gl, |
| TexImage2D(faceTarget, level, format.format, levelSize.width, |
| levelSize.height, 0, format.format, format.type, nullptr)); |
| } |
| break; |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| } |
| |
| return {}; |
| } |
| |
| MaybeError FramebufferTextureHelper(const OpenGLFunctions& gl, |
| GLenum textarget, |
| GLenum target, |
| GLenum attachment, |
| GLuint textureHandle, |
| GLuint mipLevel, |
| GLuint arrayLayer) { |
| switch (textarget) { |
| case GL_TEXTURE_2D_ARRAY: |
| case GL_TEXTURE_CUBE_MAP_ARRAY: |
| case GL_TEXTURE_3D: |
| DAWN_GL_TRY(gl, FramebufferTextureLayer(target, attachment, textureHandle, mipLevel, |
| arrayLayer)); |
| break; |
| case GL_TEXTURE_CUBE_MAP: { |
| GLenum cubeTexTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + arrayLayer; |
| DAWN_GL_TRY(gl, FramebufferTexture2D(target, attachment, cubeTexTarget, textureHandle, |
| mipLevel)); |
| break; |
| } |
| default: |
| DAWN_ASSERT(textarget == GL_TEXTURE_2D || textarget == GL_TEXTURE_2D_MULTISAMPLE); |
| DAWN_GL_TRY( |
| gl, FramebufferTexture2D(target, attachment, textarget, textureHandle, mipLevel)); |
| break; |
| } |
| return {}; |
| } |
| |
| } // namespace |
| |
| // Texture |
| |
| // static |
| ResultOrError<Ref<Texture>> Texture::Create(Device* device, |
| const UnpackedPtr<TextureDescriptor>& descriptor) { |
| // Wrap the handle in a Texture class early so that it is deleted if initialization fails |
| Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, 0, OwnsHandle::Yes)); |
| |
| bool clear = device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting); |
| DAWN_TRY(device->EnqueueGL([texture, clear](const OpenGLFunctions& gl) -> MaybeError { |
| DAWN_GL_TRY(gl, GenTextures(1, &texture->mHandle)); |
| |
| GLenum target = texture->GetGLTarget(); |
| uint32_t levels = texture->GetNumMipLevels(); |
| const GLFormat& glFormat = texture->GetGLFormat(); |
| |
| DAWN_GL_TRY(gl, BindTexture(target, texture->mHandle)); |
| DAWN_TRY(AllocateTexture(gl, target, texture->GetSampleCount(), levels, glFormat, |
| texture->GetBaseSize())); |
| |
| // The texture is not complete if it uses mipmapping and not all levels up to |
| // MAX_LEVEL have been defined. |
| DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1)); |
| |
| if (clear) { |
| DAWN_TRY(texture->ClearTexture(gl, texture->GetAllSubresources(), |
| TextureBase::ClearValue::NonZero)); |
| } |
| return {}; |
| })); |
| return std::move(texture); |
| } |
| |
| // static |
| ResultOrError<Ref<Texture>> Texture::CreateFromSharedTextureMemory( |
| SharedTextureMemory* memory, |
| const UnpackedPtr<TextureDescriptor>& descriptor) { |
| Device* device = ToBackend(memory->GetDevice()); |
| |
| Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, 0, OwnsHandle::Yes)); |
| DAWN_TRY(device->EnqueueGL([texture, memory = Ref<SharedTextureMemory>(memory)]( |
| const OpenGLFunctions& gl) -> MaybeError { |
| DAWN_TRY_ASSIGN(texture->mHandle, memory->GenerateGLTexture(gl)); |
| return {}; |
| })); |
| |
| texture->mSharedResourceMemoryContents = memory->GetContents(); |
| return texture; |
| } |
| |
| Texture::Texture(Device* device, |
| const UnpackedPtr<TextureDescriptor>& descriptor, |
| GLuint handle, |
| OwnsHandle ownsHandle) |
| : TextureBase(device, descriptor), mHandle(handle), mOwnsHandle(ownsHandle) { |
| mTarget = TargetForTextureViewDimension(GetCompatibilityTextureBindingViewDimension(), |
| descriptor->sampleCount); |
| } |
| |
| Texture::~Texture() {} |
| |
| void Texture::DestroyImpl(DestroyReason reason) { |
| TextureBase::DestroyImpl(reason); |
| if (mOwnsHandle == OwnsHandle::Yes) { |
| IgnoreErrors( |
| ToBackend(GetDevice()) |
| ->EnqueueDestroyGL(this, &Texture::GetHandle, reason, |
| [](const OpenGLFunctions& gl, GLuint handle) -> MaybeError { |
| DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle)); |
| return {}; |
| })); |
| } |
| } |
| |
| 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 OpenGLFunctions& gl, |
| const SubresourceRange& range, |
| TextureBase::ClearValue clearValue) { |
| Device* device = ToBackend(GetDevice()); |
| |
| 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) { |
| DAWN_GL_TRY(gl, DepthMask(GL_TRUE)); |
| } |
| if (range.aspects & Aspect::Stencil) { |
| DAWN_GL_TRY(gl, StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format))); |
| } |
| |
| auto DoClear = [&](Aspect aspects) -> MaybeError { |
| if (aspects == (Aspect::Depth | Aspect::Stencil)) { |
| DAWN_GL_TRY(gl, ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil)); |
| } else if (aspects == Aspect::Depth) { |
| DAWN_GL_TRY(gl, ClearBufferfv(GL_DEPTH, 0, &depth)); |
| } else if (aspects == Aspect::Stencil) { |
| DAWN_GL_TRY(gl, ClearBufferiv(GL_STENCIL, 0, &stencil)); |
| } else { |
| DAWN_UNREACHABLE(); |
| } |
| return {}; |
| }; |
| |
| GLuint framebuffer = 0; |
| DAWN_GL_TRY(gl, GenFramebuffers(1, &framebuffer)); |
| DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer)); |
| DAWN_GL_TRY(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) { |
| 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; |
| } |
| DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER, attachment, |
| GetHandle(), level, layer)); |
| DAWN_TRY(DoClear(aspectsToClear)); |
| } |
| } |
| |
| DAWN_GL_TRY(gl, Enable(GL_SCISSOR_TEST)); |
| DAWN_GL_TRY(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(); |
| const auto dimension = GetDimension(); |
| 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)) { |
| DAWN_GL_TRY(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; |
| DAWN_GL_TRY(gl, GenFramebuffers(1, &framebuffer)); |
| DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer)); |
| |
| GLenum attachment = GL_COLOR_ATTACHMENT0; |
| DAWN_GL_TRY(gl, DrawBuffers(1, &attachment)); |
| |
| DAWN_GL_TRY(gl, Disable(GL_SCISSOR_TEST)); |
| DAWN_GL_TRY(gl, ColorMask(true, true, true, true)); |
| |
| auto DoClear = [&]() -> MaybeError { |
| switch (baseType) { |
| case TextureComponentType::Float: { |
| DAWN_GL_TRY( |
| gl, ClearBufferfv(GL_COLOR, 0, |
| clearValue == TextureBase::ClearValue::Zero |
| ? kClearColorDataFloat0.data() |
| : kClearColorDataFloat1.data())); |
| break; |
| } |
| case TextureComponentType::Uint: { |
| DAWN_GL_TRY( |
| gl, ClearBufferuiv(GL_COLOR, 0, |
| clearValue == TextureBase::ClearValue::Zero |
| ? kClearColorDataUint0.data() |
| : kClearColorDataUint1.data())); |
| break; |
| } |
| case TextureComponentType::Sint: { |
| DAWN_GL_TRY(gl, ClearBufferiv( |
| GL_COLOR, 0, |
| reinterpret_cast<const GLint*>( |
| clearValue == TextureBase::ClearValue::Zero |
| ? kClearColorDataUint0.data() |
| : kClearColorDataUint1.data()))); |
| break; |
| } |
| } |
| return {}; |
| }; |
| |
| if (dimension == wgpu::TextureDimension::e3D) { |
| uint32_t depth = |
| GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color) |
| .depthOrArrayLayers; |
| for (GLint z = 0; z < static_cast<GLint>(depth); ++z) { |
| DAWN_GL_TRY(gl, FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, |
| GetHandle(), level, z)); |
| DAWN_TRY(DoClear()); |
| } |
| } else { |
| DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER, |
| attachment, GetHandle(), level, layer)); |
| DAWN_TRY(DoClear()); |
| } |
| |
| DAWN_GL_TRY(gl, Enable(GL_SCISSOR_TEST)); |
| DAWN_GL_TRY(gl, DeleteFramebuffers(1, &framebuffer)); |
| DAWN_GL_TRY(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()); |
| |
| DAWN_GL_TRY(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; |
| |
| TexelCopyBufferLayout dataLayout; |
| dataLayout.offset = 0; |
| dataLayout.bytesPerRow = bytesPerRow; |
| dataLayout.rowsPerImage = largestMipSize.height / blockInfo.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 = TexelCount{layer}; |
| DAWN_TRY(DoTexSubImage(gl, textureCopy, 0, dataLayout, mipSize)); |
| } |
| } |
| DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)); |
| } |
| if (clearValue == TextureBase::ClearValue::Zero) { |
| SetIsSubresourceContentInitialized(true, range); |
| device->IncrementLazyClearCountForTesting(); |
| } |
| return {}; |
| } |
| |
| MaybeError Texture::EnsureSubresourceContentInitialized(const OpenGLFunctions& gl, |
| const SubresourceRange& range) { |
| if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { |
| return {}; |
| } |
| if (!IsSubresourceContentInitialized(range)) { |
| DAWN_TRY(ClearTexture(gl, range, TextureBase::ClearValue::Zero)); |
| } |
| return {}; |
| } |
| |
| MaybeError Texture::SynchronizeTextureBeforeUse() { |
| SharedTextureMemoryBase::PendingFenceList fences; |
| SharedResourceMemoryContents* contents = GetSharedResourceMemoryContents(); |
| if (contents != nullptr) { |
| contents->AcquirePendingFences(&fences); |
| } |
| for (const auto& fenceAndSignaledValue : fences) { |
| SharedFence* fence = ToBackend(fenceAndSignaledValue.object).Get(); |
| DAWN_TRY(fence->ServerWait(fenceAndSignaledValue.signaledValue)); |
| } |
| |
| mLastSharedTextureMemoryUsageSerial = GetDevice()->GetQueue()->GetPendingCommandSerial(); |
| return {}; |
| } |
| |
| // TextureView |
| |
| // static |
| ResultOrError<Ref<TextureView>> TextureView::Create( |
| TextureBase* texture, |
| const UnpackedPtr<TextureViewDescriptor>& descriptor) { |
| // Wrap the handle in a TextureView class early so that it is deleted if initialization fails |
| Ref<TextureView> view = AcquireRef(new TextureView(texture, descriptor)); |
| if (RequiresCreatingNewTextureView(texture, descriptor)) { |
| view->mOwnsHandle = OwnsHandle::Yes; |
| } else { |
| view->mOwnsHandle = OwnsHandle::No; |
| } |
| DAWN_TRY(ToBackend(texture->GetDevice()) |
| ->EnqueueGL([view, texture = Ref<Texture>(ToBackend(texture))]( |
| const OpenGLFunctions& gl) -> MaybeError { |
| if (texture->IsDestroyed()) { |
| view->mHandle = 0; |
| } else if (view->mOwnsHandle == OwnsHandle::Yes) { |
| GLuint handle = 0; |
| DAWN_ASSERT(gl.IsAtLeastGL(4, 3)); |
| DAWN_GL_TRY(gl, GenTextures(1, &handle)); |
| DAWN_GL_TRY(gl, |
| TextureView(handle, view->GetGLTarget(), texture->GetHandle(), |
| view->GetInternalFormat(), view->GetBaseMipLevel(), |
| view->GetLevelCount(), view->GetBaseArrayLayer(), |
| view->GetLayerCount())); |
| view->mHandle = handle; |
| } else { |
| view->mHandle = texture->GetHandle(); |
| } |
| return {}; |
| })); |
| return view; |
| } |
| |
| TextureView::TextureView(TextureBase* texture, const UnpackedPtr<TextureViewDescriptor>& descriptor) |
| : TextureViewBase(texture, descriptor) { |
| mTarget = TargetForTextureViewDimension(descriptor->dimension, texture->GetSampleCount()); |
| } |
| |
| TextureView::~TextureView() {} |
| |
| void TextureView::DestroyImpl(DestroyReason reason) { |
| TextureViewBase::DestroyImpl(reason); |
| if (mOwnsHandle == OwnsHandle::Yes) { |
| IgnoreErrors( |
| ToBackend(GetDevice()) |
| ->EnqueueDestroyGL(this, &TextureView::GetHandle, reason, |
| [](const OpenGLFunctions& gl, GLuint handle) -> MaybeError { |
| DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle)); |
| return {}; |
| })); |
| } |
| } |
| |
| GLuint TextureView::GetHandle() const { |
| DAWN_ASSERT(mHandle != 0); |
| return mHandle; |
| } |
| |
| GLenum TextureView::GetGLTarget() const { |
| return mTarget; |
| } |
| |
| MaybeError TextureView::BindToFramebuffer(const OpenGLFunctions& gl, |
| GLenum target, |
| GLenum attachment, |
| GLuint depthSlice) { |
| DAWN_ASSERT(depthSlice < |
| static_cast<GLuint>(GetSingleSubresourceVirtualSize().depthOrArrayLayers)); |
| |
| // 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(); |
| |
| GLenum textarget; |
| GLuint textureHandle, mipLevel, arrayLayer; |
| if (useOwnView) { |
| // Use our own texture handle and target which points to a subset of the texture's |
| // subresources. |
| textureHandle = 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 |
| |
| textureHandle = ToBackend(GetTexture())->GetHandle(); |
| textarget = ToBackend(GetTexture())->GetGLTarget(); |
| mipLevel = GetBaseMipLevel(); |
| // We have validated that the depthSlice in render pass's colorAttachments must be undefined |
| // for 2d RTVs, which value is set to 0. For 3d RTVs, the baseArrayLayer must be 0. So here |
| // we can simply use baseArrayLayer + depthSlice to specify the slice in RTVs without |
| // checking the view's dimension. |
| arrayLayer = GetBaseArrayLayer() + depthSlice; |
| } |
| |
| DAWN_ASSERT(textureHandle != 0); |
| |
| return FramebufferTextureHelper(gl, textarget, target, attachment, textureHandle, mipLevel, |
| arrayLayer); |
| } |
| |
| 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 |