Implement rendering with view format reinterpretation

Includes fixes for the backends to create and use a texture view
if reinterpretation is required. Multiple backends used the original
texture and its format instead of the view.

Bug: dawn:1276
Change-Id: Ic31231b2955314e90e011905c9048db6f7899299
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/84704
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Austin Eng 2022-04-07 12:14:55 +00:00 committed by Dawn LUCI CQ
parent 2787a59fcd
commit e1f2dcd3b3
10 changed files with 372 additions and 87 deletions

View File

@ -315,10 +315,11 @@ 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,
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;
@ -338,10 +339,8 @@ namespace dawn::native {
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];

View File

@ -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,

View File

@ -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,

View File

@ -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<MTLTexture> 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:

View File

@ -46,7 +46,7 @@ namespace dawn::native::metal {
const TextureDescriptor* descriptor,
NSPRef<id<MTLTexture>> wrapped);
id<MTLTexture> GetMTLTexture();
id<MTLTexture> GetMTLTexture() const;
IOSurfaceRef GetIOSurface();
NSPRef<id<MTLTexture>> CreateFormatView(wgpu::TextureFormat format);
@ -83,12 +83,20 @@ namespace dawn::native::metal {
static ResultOrError<Ref<TextureView>> Create(TextureBase* texture,
const TextureViewDescriptor* descriptor);
id<MTLTexture> GetMTLTexture();
id<MTLTexture> GetMTLTexture() const;
struct AttachmentInfo {
NSPRef<id<MTLTexture>> 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<id<MTLTexture>> mMtlTextureView;
};

View File

@ -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<MTLTexture> Texture::GetMTLTexture() {
id<MTLTexture> Texture::GetMTLTexture() const {
return mMtlTexture.Get();
}
@ -1026,9 +1041,7 @@ namespace dawn::native::metal {
id<MTLTexture> 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<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
@ -1112,8 +1125,30 @@ namespace dawn::native::metal {
return {};
}
id<MTLTexture> TextureView::GetMTLTexture() {
id<MTLTexture> 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

View File

@ -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);
}

View File

@ -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;

View File

@ -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<RGBA8, 4> 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<f32> {
var pos = array<vec2<f32>, 6>(
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>( 1.0, 1.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
)");
pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
@group(0) @binding(0) var texture : texture_2d<f32>;
@stage(fragment)
fn main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
return textureLoad(texture, vec2<i32>(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<RGBA8, 4> 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<f32> {
var pos = array<vec2<f32>, 6>(
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>( 1.0, 1.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
)");
pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
@group(0) @binding(0) var texture : texture_2d<f32>;
@stage(fragment)
fn main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
return textureLoad(texture, vec2<i32>(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());