diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp index c20172936d..946baf5a60 100644 --- a/src/dawn/native/Format.cpp +++ b/src/dawn/native/Format.cpp @@ -315,39 +315,38 @@ namespace dawn::native { AddFormat(internalFormat); }; - auto AddMultiAspectFormat = - [&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects, - wgpu::TextureFormat firstFormat, wgpu::TextureFormat secondFormat, - bool isRenderable, bool isSupported, bool supportsMultisample, - uint8_t componentCount) { - Format internalFormat; - internalFormat.format = format; - internalFormat.baseFormat = format; - internalFormat.isRenderable = isRenderable; - internalFormat.isCompressed = false; - internalFormat.isSupported = isSupported; - internalFormat.supportsStorageUsage = false; - internalFormat.supportsMultisample = supportsMultisample; - internalFormat.supportsResolveTarget = false; - internalFormat.aspects = aspects; - internalFormat.componentCount = componentCount; + auto AddMultiAspectFormat = [&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects, + wgpu::TextureFormat firstFormat, + wgpu::TextureFormat secondFormat, + bool isRenderable, bool isSupported, + bool supportsMultisample, + uint8_t componentCount) { + Format internalFormat; + internalFormat.format = format; + internalFormat.baseFormat = format; + internalFormat.isRenderable = isRenderable; + internalFormat.isCompressed = false; + internalFormat.isSupported = isSupported; + internalFormat.supportsStorageUsage = false; + internalFormat.supportsMultisample = supportsMultisample; + internalFormat.supportsResolveTarget = false; + internalFormat.aspects = aspects; + internalFormat.componentCount = componentCount; - // Multi aspect formats just copy information about single-aspect formats. This - // means that the single-plane formats must have been added before multi-aspect - // ones. (it is ASSERTed below). - const FormatIndex firstFormatIndex = ComputeFormatIndex(firstFormat); - const FormatIndex secondFormatIndex = ComputeFormatIndex(secondFormat); + // Multi aspect formats just copy information about single-aspect formats. This + // means that the single-plane formats must have been added before multi-aspect + // ones. (it is ASSERTed below). + const FormatIndex firstFormatIndex = ComputeFormatIndex(firstFormat); + const FormatIndex secondFormatIndex = ComputeFormatIndex(secondFormat); - ASSERT(table[firstFormatIndex].aspectInfo[0].format != - wgpu::TextureFormat::Undefined); - ASSERT(table[secondFormatIndex].aspectInfo[0].format != - wgpu::TextureFormat::Undefined); + ASSERT(table[firstFormatIndex].aspectInfo[0].format != wgpu::TextureFormat::Undefined); + ASSERT(table[secondFormatIndex].aspectInfo[0].format != wgpu::TextureFormat::Undefined); - internalFormat.aspectInfo[0] = table[firstFormatIndex].aspectInfo[0]; - internalFormat.aspectInfo[1] = table[secondFormatIndex].aspectInfo[0]; + internalFormat.aspectInfo[0] = table[firstFormatIndex].aspectInfo[0]; + internalFormat.aspectInfo[1] = table[secondFormatIndex].aspectInfo[0]; - AddFormat(internalFormat); - }; + AddFormat(internalFormat); + }; // clang-format off // 1 byte color formats diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp index 5e7941767a..b86abbde80 100644 --- a/src/dawn/native/d3d12/TextureD3D12.cpp +++ b/src/dawn/native/d3d12/TextureD3D12.cpp @@ -909,11 +909,12 @@ namespace dawn::native::d3d12 { }); } - D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel, + D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(const Format& format, + uint32_t mipLevel, uint32_t baseSlice, uint32_t sliceCount) const { D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; - rtvDesc.Format = GetD3D12Format(); + rtvDesc.Format = D3D12TextureFormat(format.format); if (IsMultisampledTexture()) { ASSERT(GetDimension() == wgpu::TextureDimension::e2D); ASSERT(GetNumMipLevels() == 1); @@ -1070,7 +1071,7 @@ namespace dawn::native::d3d12 { sliceCount = std::max(GetDepth() >> level, 1u); } D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = - GetRTVDescriptor(level, baseSlice, sliceCount); + GetRTVDescriptor(GetFormat(), level, baseSlice, sliceCount); device->GetD3D12Device()->CreateRenderTargetView(GetD3D12Resource(), &rtvDesc, rtvHandle); commandList->ClearRenderTargetView(rtvHandle, clearColorRGBA, 0, nullptr); @@ -1340,7 +1341,8 @@ namespace dawn::native::d3d12 { D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() const { return ToBackend(GetTexture()) - ->GetRTVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount()); + ->GetRTVDescriptor(GetFormat(), GetBaseMipLevel(), GetBaseArrayLayer(), + GetLayerCount()); } D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor(bool depthReadOnly, diff --git a/src/dawn/native/d3d12/TextureD3D12.h b/src/dawn/native/d3d12/TextureD3D12.h index 76572bab59..1264f7628d 100644 --- a/src/dawn/native/d3d12/TextureD3D12.h +++ b/src/dawn/native/d3d12/TextureD3D12.h @@ -55,7 +55,8 @@ namespace dawn::native::d3d12 { ID3D12Resource* GetD3D12Resource() const; DXGI_FORMAT GetD3D12CopyableSubresourceFormat(Aspect aspect) const; - D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t mipLevel, + D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(const Format& format, + uint32_t mipLevel, uint32_t baseSlice, uint32_t sliceCount) const; D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(uint32_t mipLevel, diff --git a/src/dawn/native/metal/CommandBufferMTL.mm b/src/dawn/native/metal/CommandBufferMTL.mm index 86b88a8e83..fd232b4687 100644 --- a/src/dawn/native/metal/CommandBufferMTL.mm +++ b/src/dawn/native/metal/CommandBufferMTL.mm @@ -87,19 +87,18 @@ namespace dawn::native::metal { break; } - descriptor.colorAttachments[i].texture = - ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture(); - descriptor.colorAttachments[i].level = attachmentInfo.view->GetBaseMipLevel(); - descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer(); + auto colorAttachment = ToBackend(attachmentInfo.view)->GetAttachmentInfo(); + descriptor.colorAttachments[i].texture = colorAttachment.texture.Get(); + descriptor.colorAttachments[i].level = colorAttachment.baseMipLevel; + descriptor.colorAttachments[i].slice = colorAttachment.baseArrayLayer; bool hasResolveTarget = attachmentInfo.resolveTarget != nullptr; if (hasResolveTarget) { - descriptor.colorAttachments[i].resolveTexture = - ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture(); - descriptor.colorAttachments[i].resolveLevel = - attachmentInfo.resolveTarget->GetBaseMipLevel(); - descriptor.colorAttachments[i].resolveSlice = - attachmentInfo.resolveTarget->GetBaseArrayLayer(); + auto resolveAttachment = + ToBackend(attachmentInfo.resolveTarget)->GetAttachmentInfo(); + descriptor.colorAttachments[i].resolveTexture = resolveAttachment.texture.Get(); + descriptor.colorAttachments[i].resolveLevel = resolveAttachment.baseMipLevel; + descriptor.colorAttachments[i].resolveSlice = resolveAttachment.baseArrayLayer; switch (attachmentInfo.storeOp) { case wgpu::StoreOp::Store: @@ -132,14 +131,13 @@ namespace dawn::native::metal { if (renderPass->attachmentState->HasDepthStencilAttachment()) { auto& attachmentInfo = renderPass->depthStencilAttachment; - id texture = - ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture(); - const Format& format = attachmentInfo.view->GetTexture()->GetFormat(); + auto depthStencilAttachment = ToBackend(attachmentInfo.view)->GetAttachmentInfo(); + const Format& format = attachmentInfo.view->GetFormat(); if (format.HasDepth()) { - descriptor.depthAttachment.texture = texture; - descriptor.depthAttachment.level = attachmentInfo.view->GetBaseMipLevel(); - descriptor.depthAttachment.slice = attachmentInfo.view->GetBaseArrayLayer(); + descriptor.depthAttachment.texture = depthStencilAttachment.texture.Get(); + descriptor.depthAttachment.level = depthStencilAttachment.baseMipLevel; + descriptor.depthAttachment.slice = depthStencilAttachment.baseArrayLayer; switch (attachmentInfo.depthStoreOp) { case wgpu::StoreOp::Store: @@ -172,9 +170,9 @@ namespace dawn::native::metal { } if (format.HasStencil()) { - descriptor.stencilAttachment.texture = texture; - descriptor.stencilAttachment.level = attachmentInfo.view->GetBaseMipLevel(); - descriptor.stencilAttachment.slice = attachmentInfo.view->GetBaseArrayLayer(); + descriptor.stencilAttachment.texture = depthStencilAttachment.texture.Get(); + descriptor.stencilAttachment.level = depthStencilAttachment.baseMipLevel; + descriptor.stencilAttachment.slice = depthStencilAttachment.baseArrayLayer; switch (attachmentInfo.stencilStoreOp) { case wgpu::StoreOp::Store: diff --git a/src/dawn/native/metal/TextureMTL.h b/src/dawn/native/metal/TextureMTL.h index ba7f97bc2b..c5544f6857 100644 --- a/src/dawn/native/metal/TextureMTL.h +++ b/src/dawn/native/metal/TextureMTL.h @@ -46,7 +46,7 @@ namespace dawn::native::metal { const TextureDescriptor* descriptor, NSPRef> wrapped); - id GetMTLTexture(); + id GetMTLTexture() const; IOSurfaceRef GetIOSurface(); NSPRef> CreateFormatView(wgpu::TextureFormat format); @@ -83,12 +83,20 @@ namespace dawn::native::metal { static ResultOrError> Create(TextureBase* texture, const TextureViewDescriptor* descriptor); - id GetMTLTexture(); + id GetMTLTexture() const; + + struct AttachmentInfo { + NSPRef> texture; + uint32_t baseMipLevel; + uint32_t baseArrayLayer; + }; + AttachmentInfo GetAttachmentInfo() const; private: using TextureViewBase::TextureViewBase; MaybeError Initialize(const TextureViewDescriptor* descriptor); + // TODO(crbug.com/dawn/1355): Clear this reference on texture destroy. NSPRef> mMtlTextureView; }; diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm index c6fd75e87e..dc04324a9b 100644 --- a/src/dawn/native/metal/TextureMTL.mm +++ b/src/dawn/native/metal/TextureMTL.mm @@ -28,11 +28,6 @@ namespace dawn::native::metal { namespace { - bool UsageNeedsTextureView(wgpu::TextureUsage usage) { - constexpr wgpu::TextureUsage kUsageNeedsTextureView = - wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding; - return usage & kUsageNeedsTextureView; - } MTLTextureUsage MetalTextureUsage(const Format& format, wgpu::TextureUsage usage, @@ -46,8 +41,10 @@ namespace dawn::native::metal { if (usage & (wgpu::TextureUsage::TextureBinding)) { result |= MTLTextureUsageShaderRead; - // For sampling stencil aspect of combined depth/stencil. See TextureView - // constructor. + // For sampling stencil aspect of combined depth/stencil. + // See TextureView::Initialize. + // Depth views for depth/stencil textures in Metal simply use the original + // texture's format, but stencil views require format reinterpretation. if (@available(macOS 10.12, iOS 10.0, *)) { if (IsSubset(Aspect::Depth | Aspect::Stencil, format.aspects)) { result |= MTLTextureUsagePixelFormatView; @@ -86,13 +83,28 @@ namespace dawn::native::metal { 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. + // Color format reinterpretation required. + // Note: Depth/stencil formats don't support reinterpretation. + // See also TextureView::GetAttachmentInfo when modifying this condition. 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 && @@ -107,8 +119,11 @@ namespace dawn::native::metal { return true; } - if (IsSubset(Aspect::Depth | Aspect::Stencil, texture->GetFormat().aspects) && - textureViewDescriptor->aspect == wgpu::TextureAspect::StencilOnly) { + // If the texture is created with MTLTextureUsagePixelFormatView, we need + // a new view to perform format reinterpretation. + if ((MetalTextureUsage(texture->GetFormat(), texture->GetInternalUsage(), + texture->GetSampleCount()) & + MTLTextureUsagePixelFormatView) != 0) { return true; } @@ -776,7 +791,7 @@ namespace dawn::native::metal { mIOSurface = nullptr; } - id Texture::GetMTLTexture() { + id Texture::GetMTLTexture() const { return mMtlTexture.Get(); } @@ -1026,9 +1041,7 @@ namespace dawn::native::metal { id mtlTexture = texture->GetMTLTexture(); - if (!UsageNeedsTextureView(texture->GetInternalUsage())) { - mMtlTextureView = nullptr; - } else if (!RequiresCreatingNewTextureView(texture, descriptor)) { + if (!RequiresCreatingNewTextureView(texture, descriptor)) { mMtlTextureView = mtlTexture; } else if (texture->GetFormat().IsMultiPlanar()) { NSRef mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]); @@ -1112,8 +1125,30 @@ namespace dawn::native::metal { return {}; } - id TextureView::GetMTLTexture() { + id TextureView::GetMTLTexture() const { ASSERT(mMtlTextureView != nullptr); return mMtlTextureView.Get(); } + + TextureView::AttachmentInfo TextureView::GetAttachmentInfo() const { + ASSERT(GetTexture()->GetInternalUsage() & wgpu::TextureUsage::RenderAttachment); + // Use our own view if the formats do not match. + // If the formats do not match, format reinterpretation will be required. + // Note: Depth/stencil formats don't support reinterpretation. + // Also, we compute |useOwnView| here instead of relying on whether or not + // a view was created in Initialize, because rendering to a depth/stencil + // texture on Metal only works when using the original texture, not a view. + bool useOwnView = GetFormat().format != GetTexture()->GetFormat().format && + !GetTexture()->GetFormat().HasDepthOrStencil(); + if (useOwnView) { + ASSERT(mMtlTextureView.Get()); + return {mMtlTextureView, 0, 0}; + } + AttachmentInfo info; + info.texture = ToBackend(GetTexture())->GetMTLTexture(); + info.baseMipLevel = GetBaseMipLevel(); + info.baseArrayLayer = GetBaseArrayLayer(); + return info; + } + } // namespace dawn::native::metal diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp index fc4431fc0e..0f5bf7b934 100644 --- a/src/dawn/native/opengl/TextureGL.cpp +++ b/src/dawn/native/opengl/TextureGL.cpp @@ -87,14 +87,16 @@ namespace dawn::native::opengl { return handle; } - bool UsageNeedsTextureView(wgpu::TextureUsage usage) { - constexpr wgpu::TextureUsage kUsageNeedingTextureView = - wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding; - return usage & kUsageNeedingTextureView; - } - 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 @@ -102,6 +104,12 @@ namespace dawn::native::opengl { 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 && @@ -554,9 +562,7 @@ namespace dawn::native::opengl { return; } - if (!UsageNeedsTextureView(texture->GetUsage())) { - mHandle = 0; - } else if (!RequiresCreatingNewTextureView(texture, descriptor)) { + if (!RequiresCreatingNewTextureView(texture, descriptor)) { mHandle = ToBackend(texture)->GetHandle(); } else { // glTextureView() is supported on OpenGL version >= 4.3 @@ -601,13 +607,26 @@ namespace dawn::native::opengl { void TextureView::BindToFramebuffer(GLenum target, GLenum attachment) { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; - // Use the texture's handle and target, and the view's base mip level and base array layer - GLuint handle = ToBackend(GetTexture())->GetHandle(); - GLuint textarget = ToBackend(GetTexture())->GetGLTarget(); - GLuint mipLevel = GetBaseMipLevel(); + GLuint handle, textarget, mipLevel, arrayLayer; + if (mOwnsHandle) { + // 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 + // layer. + handle = ToBackend(GetTexture())->GetHandle(); + textarget = ToBackend(GetTexture())->GetGLTarget(); + mipLevel = GetBaseMipLevel(); + arrayLayer = GetBaseArrayLayer(); + } + ASSERT(handle != 0); if (textarget == GL_TEXTURE_2D_ARRAY || textarget == GL_TEXTURE_3D) { - gl.FramebufferTextureLayer(target, attachment, handle, mipLevel, GetBaseArrayLayer()); + gl.FramebufferTextureLayer(target, attachment, handle, mipLevel, arrayLayer); } else { gl.FramebufferTexture2D(target, attachment, textarget, handle, mipLevel); } diff --git a/src/dawn/native/opengl/TextureGL.h b/src/dawn/native/opengl/TextureGL.h index 897022ca9e..2b0b4b119b 100644 --- a/src/dawn/native/opengl/TextureGL.h +++ b/src/dawn/native/opengl/TextureGL.h @@ -60,6 +60,7 @@ namespace dawn::native::opengl { ~TextureView() override; void DestroyImpl() override; + // TODO(crbug.com/dawn/1355): Delete this handle on texture destroy. GLuint mHandle; GLenum mTarget; bool mOwnsHandle; diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp index c09cb0d12e..cd68c9a2a0 100644 --- a/src/dawn/native/vulkan/SwapChainVk.cpp +++ b/src/dawn/native/vulkan/SwapChainVk.cpp @@ -433,7 +433,7 @@ namespace dawn::native::vulkan { config.alphaMode = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; #if !defined(DAWN_PLATFORM_ANDROID) DAWN_INVALID_IF((surfaceInfo.capabilities.supportedCompositeAlpha & - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) == 0, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) == 0, "Vulkan SwapChain must support opaque alpha."); #else // TODO(dawn:286): investigate composite alpha for WebGPU native diff --git a/src/dawn/tests/end2end/TextureViewTests.cpp b/src/dawn/tests/end2end/TextureViewTests.cpp index 9c2a868bf3..d3746f4a92 100644 --- a/src/dawn/tests/end2end/TextureViewTests.cpp +++ b/src/dawn/tests/end2end/TextureViewTests.cpp @@ -438,7 +438,7 @@ TEST_P(TextureViewSamplingTest, Texture2DArrayViewOnOneLevelOf2DArrayTexture) { Texture2DArrayViewTest(6, 6, 2, 4); } -// Test that an RGBA8 texture may be interpreted as RGBA8UnormSrgb +// Test that an RGBA8 texture may be interpreted as RGBA8UnormSrgb and sampled from. // More extensive color value checks and format tests are left for the CTS. TEST_P(TextureViewSamplingTest, SRGBReinterpretation) { // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES. @@ -772,6 +772,226 @@ TEST_P(TextureViewRenderingTest, Texture2DArrayViewOnALayerOf2DArrayTextureAsCol } } +// Test that an RGBA8 texture may be interpreted as RGBA8UnormSrgb and rendered to. +// More extensive color value checks and format tests are left for the CTS. +TEST_P(TextureViewRenderingTest, SRGBReinterpretationRenderAttachment) { + // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES. + DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES()); + + // Test will render into an SRGB view + wgpu::TextureViewDescriptor viewDesc = {}; + viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb; + + // Make an RGBA8Unorm texture to back the SRGB view. + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.size = {2, 2, 1}; + textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment; + textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + textureDesc.viewFormats = &viewDesc.format; + textureDesc.viewFormatCount = 1; + wgpu::Texture texture = device.CreateTexture(&textureDesc); + + // Make an RGBA8Unorm sampled texture as the source. + textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding; + wgpu::Texture sampledTexture = device.CreateTexture(&textureDesc); + + // Initial non-SRGB data to upload to |sampledTexture|. + std::array rgbaTextureData = { + RGBA8(117, 0, 0, 255), + RGBA8(0, 23, 0, 127), + RGBA8(0, 0, 12, 100), + RGBA8(13, 117, 24, 90), + }; + + wgpu::ImageCopyTexture dst = {}; + wgpu::TextureDataLayout dataLayout = {}; + + // Upload |rgbaTextureData| into |sampledTexture|. + dst.texture = sampledTexture; + dataLayout.bytesPerRow = textureDesc.size.width * sizeof(RGBA8); + queue.WriteTexture(&dst, rgbaTextureData.data(), rgbaTextureData.size() * sizeof(RGBA8), + &dataLayout, &textureDesc.size); + + // View both the attachment as SRGB. + wgpu::TextureView textureView = texture.CreateView(&viewDesc); + + // Create a render pipeline to blit |sampledTexture| into |textureView|. + utils::ComboRenderPipelineDescriptor pipelineDesc; + pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"( + @stage(vertex) + fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4 { + var pos = array, 6>( + vec2(-1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, -1.0), + vec2( 1.0, 1.0)); + return vec4(pos[VertexIndex], 0.0, 1.0); + } + )"); + pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var texture : texture_2d; + + @stage(fragment) + fn main(@builtin(position) coord: vec4) -> @location(0) vec4 { + return textureLoad(texture, vec2(coord.xy), 0); + } + )"); + pipelineDesc.cTargets[0].format = viewDesc.format; + + // Submit a render pass to perform the blit from |sampledTexture| to |textureView|. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampledTexture.CreateView()}}); + + utils::ComboRenderPassDescriptor renderPassInfo{textureView}; + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Check the results. This is the sRGB encoding for the same non-SRGB colors + // represented by |initialData|. + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(180, 0, 0, 255), // + RGBA8(181, 0, 0, 255), texture, 0, 0); + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(0, 85, 0, 127), // + RGBA8(0, 86, 0, 127), texture, 1, 0); + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(0, 0, 61, 100), // + RGBA8(0, 0, 62, 100), texture, 0, 1); + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(64, 180, 86, 90), // + RGBA8(15, 181, 87, 90), texture, 1, 1); +} + +// Test that an RGBA8 texture may be interpreted as RGBA8UnormSrgb and resolved to. +// More extensive color value checks and format tests are left for the CTS. +// This test samples the RGBA8Unorm texture into an RGBA8Unorm multisample texture viewed as SRGB, +// and resolves it into an RGBA8Unorm texture, viewed as SRGB. +TEST_P(TextureViewRenderingTest, SRGBReinterpretionResolveAttachment) { + // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES. + DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES()); + + // Test will resolve into an SRGB view + wgpu::TextureViewDescriptor viewDesc = {}; + viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb; + + // Make an RGBA8Unorm texture to back the SRGB view. + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.size = {2, 2, 1}; + textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + textureDesc.viewFormats = &viewDesc.format; + textureDesc.viewFormatCount = 1; + textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment; + wgpu::Texture resolveTexture = device.CreateTexture(&textureDesc); + + // Make an RGBA8Unorm multisampled texture for the color attachment. + textureDesc.sampleCount = 4; + textureDesc.usage = wgpu::TextureUsage::RenderAttachment; + wgpu::Texture multisampledTexture = device.CreateTexture(&textureDesc); + + // Make an RGBA8Unorm sampled texture as the source. + textureDesc.sampleCount = 1; + textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding; + wgpu::Texture sampledTexture = device.CreateTexture(&textureDesc); + + // Initial non-SRGB data to upload to |sampledTexture|. + std::array rgbaTextureData = { + RGBA8(117, 0, 0, 255), + RGBA8(0, 23, 0, 127), + RGBA8(0, 0, 12, 100), + RGBA8(13, 117, 24, 90), + }; + + wgpu::ImageCopyTexture dst = {}; + wgpu::TextureDataLayout dataLayout = {}; + + // Upload |rgbaTextureData| into |sampledTexture|. + dst.texture = sampledTexture; + dataLayout.bytesPerRow = textureDesc.size.width * sizeof(RGBA8); + queue.WriteTexture(&dst, rgbaTextureData.data(), rgbaTextureData.size() * sizeof(RGBA8), + &dataLayout, &textureDesc.size); + + // View both the multisampled texture and the resolve texture as SRGB. + wgpu::TextureView resolveView = resolveTexture.CreateView(&viewDesc); + wgpu::TextureView multisampledTextureView = multisampledTexture.CreateView(&viewDesc); + + // Create a render pipeline to blit |sampledTexture| into |multisampledTextureView|. + utils::ComboRenderPipelineDescriptor pipelineDesc; + pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"( + @stage(vertex) + fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4 { + var pos = array, 6>( + vec2(-1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, -1.0), + vec2( 1.0, 1.0)); + return vec4(pos[VertexIndex], 0.0, 1.0); + } + )"); + pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var texture : texture_2d; + + @stage(fragment) + fn main(@builtin(position) coord: vec4) -> @location(0) vec4 { + return textureLoad(texture, vec2(coord.xy), 0); + } + )"); + pipelineDesc.cTargets[0].format = viewDesc.format; + pipelineDesc.multisample.count = 4; + + // Submit a render pass to perform the blit from |sampledTexture| to |multisampledTextureView|. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampledTexture.CreateView()}}); + + utils::ComboRenderPassDescriptor renderPassInfo{multisampledTextureView}; + renderPassInfo.cColorAttachments[0].resolveTarget = resolveView; + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Check the results. This is the sRGB encoding for the same non-SRGB colors + // represented by |initialData|. + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(180, 0, 0, 255), // + RGBA8(181, 0, 0, 255), resolveTexture, 0, 0); + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(0, 85, 0, 127), // + RGBA8(0, 86, 0, 127), resolveTexture, 1, 0); + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(0, 0, 61, 100), // + RGBA8(0, 0, 62, 100), resolveTexture, 0, 1); + EXPECT_PIXEL_RGBA8_BETWEEN( // + RGBA8(64, 180, 86, 90), // + RGBA8(15, 181, 87, 90), resolveTexture, 1, 1); +} + DAWN_INSTANTIATE_TEST(TextureViewSamplingTest, D3D12Backend(), MetalBackend(), @@ -781,7 +1001,9 @@ DAWN_INSTANTIATE_TEST(TextureViewSamplingTest, DAWN_INSTANTIATE_TEST(TextureViewRenderingTest, D3D12Backend(), + D3D12Backend({}, {"use_d3d12_render_pass"}), MetalBackend(), + MetalBackend({"emulate_store_and_msaa_resolve"}), OpenGLBackend(), OpenGLESBackend(), VulkanBackend());