blob: 920562e0fa9e6c50cd8283f9f932777b78e7e175 [file] [log] [blame]
// Copyright 2023 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/BlitColorToColorWithDraw.h"
#include "dawn/common/Assert.h"
#include "dawn/common/HashUtils.h"
#include "dawn/native/BindGroup.h"
#include "dawn/native/CommandEncoder.h"
#include "dawn/native/Device.h"
#include "dawn/native/InternalPipelineStore.h"
#include "dawn/native/RenderPassEncoder.h"
#include "dawn/native/RenderPipeline.h"
#include "dawn/native/utils/WGPUHelpers.h"
namespace dawn::native {
namespace {
constexpr char kBlitToColorVS[] = R"(
@vertex fn vert_fullscreen_quad(
@builtin(vertex_index) vertex_index : u32,
) -> @builtin(position) vec4f {
const pos = array(
vec2f(-1.0, -1.0),
vec2f( 3.0, -1.0),
vec2f(-1.0, 3.0));
return vec4f(pos[vertex_index], 0.0, 1.0);
}
)";
constexpr char kBlitToFloatColorFS[] = R"(
@group(0) @binding(0) var src_tex : texture_2d<f32>;
@fragment fn blit_to_color(@builtin(position) position : vec4f) -> @location(0) vec4f {
return textureLoad(src_tex, vec2u(position.xy), 0);
}
)";
ResultOrError<Ref<RenderPipelineBase>> GetOrCreateColorBlitPipeline(
DeviceBase* device,
const Format& colorInternalFormat,
wgpu::TextureFormat depthStencilFormat,
uint32_t sampleCount) {
InternalPipelineStore* store = device->GetInternalPipelineStore();
BlitColorToColorWithDrawPipelineKey pipelineKey;
pipelineKey.colorFormat = colorInternalFormat.format;
pipelineKey.depthStencilFormat = depthStencilFormat;
pipelineKey.sampleCount = sampleCount;
{
auto it = store->msaaRenderToSingleSampledColorBlitPipelines.find(pipelineKey);
if (it != store->msaaRenderToSingleSampledColorBlitPipelines.end()) {
return it->second;
}
}
const auto& formatAspectInfo = colorInternalFormat.GetAspectInfo(Aspect::Color);
// vertex shader's source.
ShaderModuleWGSLDescriptor wgslDesc = {};
ShaderModuleDescriptor shaderModuleDesc = {};
shaderModuleDesc.nextInChain = &wgslDesc;
wgslDesc.code = kBlitToColorVS;
Ref<ShaderModuleBase> vshaderModule;
DAWN_TRY_ASSIGN(vshaderModule, device->CreateShaderModule(&shaderModuleDesc));
// fragment shader's source will depend on color format type.
switch (formatAspectInfo.baseType) {
case TextureComponentType::Float:
wgslDesc.code = kBlitToFloatColorFS;
break;
default:
// TODO(dawn:1710): blitting integer textures are not currently supported.
DAWN_UNREACHABLE();
break;
}
Ref<ShaderModuleBase> fshaderModule;
DAWN_TRY_ASSIGN(fshaderModule, device->CreateShaderModule(&shaderModuleDesc));
FragmentState fragmentState = {};
fragmentState.module = fshaderModule.Get();
fragmentState.entryPoint = "blit_to_color";
// Color target state.
ColorTargetState colorTarget;
colorTarget.format = colorInternalFormat.format;
fragmentState.targetCount = 1;
fragmentState.targets = &colorTarget;
RenderPipelineDescriptor renderPipelineDesc = {};
renderPipelineDesc.label = "blit_color_to_color";
renderPipelineDesc.vertex.module = vshaderModule.Get();
renderPipelineDesc.vertex.entryPoint = "vert_fullscreen_quad";
renderPipelineDesc.fragment = &fragmentState;
// Depth stencil state.
DepthStencilState depthStencilState = {};
if (depthStencilFormat != wgpu::TextureFormat::Undefined) {
depthStencilState.format = depthStencilFormat;
depthStencilState.depthWriteEnabled = false;
depthStencilState.depthCompare = wgpu::CompareFunction::Always;
renderPipelineDesc.depthStencil = &depthStencilState;
}
// Multisample state.
DAWN_ASSERT(sampleCount > 1);
renderPipelineDesc.multisample.count = sampleCount;
// Bind group layout.
Ref<BindGroupLayoutBase> bindGroupLayout;
DAWN_TRY_ASSIGN(bindGroupLayout,
utils::MakeBindGroupLayout(
device,
{
{0, wgpu::ShaderStage::Fragment, kInternalResolveAttachmentSampleType,
wgpu::TextureViewDimension::e2D},
},
/* allowInternalBinding */ true));
Ref<PipelineLayoutBase> pipelineLayout;
DAWN_TRY_ASSIGN(pipelineLayout, utils::MakeBasicPipelineLayout(device, bindGroupLayout));
renderPipelineDesc.layout = pipelineLayout.Get();
Ref<RenderPipelineBase> pipeline;
DAWN_TRY_ASSIGN(pipeline, device->CreateRenderPipeline(&renderPipelineDesc));
store->msaaRenderToSingleSampledColorBlitPipelines.emplace(pipelineKey, pipeline);
return pipeline;
}
} // namespace
MaybeError ExpandResolveTextureWithDraw(DeviceBase* device,
RenderPassEncoder* renderEncoder,
const RenderPassDescriptor* renderPassDescriptor) {
DAWN_ASSERT(device->IsLockedByCurrentThreadIfNeeded());
DAWN_ASSERT(device->IsResolveTextureBlitWithDrawSupported());
// TODO(dawn:1710): support multiple attachments.
DAWN_ASSERT(renderPassDescriptor->colorAttachmentCount == 1);
// The original color attachment of the render pass will be used as source.
TextureViewBase* src = renderPassDescriptor->colorAttachments[0].resolveTarget;
TextureViewBase* dst = renderPassDescriptor->colorAttachments[0].view;
TextureBase* dstTexture = dst->GetTexture();
DAWN_ASSERT(src->GetLayerCount() == 1u);
DAWN_ASSERT(src->GetDimension() == wgpu::TextureViewDimension::e2D);
wgpu::TextureFormat depthStencilFormat = wgpu::TextureFormat::Undefined;
if (renderPassDescriptor->depthStencilAttachment != nullptr) {
depthStencilFormat = renderPassDescriptor->depthStencilAttachment->view->GetFormat().format;
}
Ref<RenderPipelineBase> pipeline;
DAWN_TRY_ASSIGN(pipeline,
GetOrCreateColorBlitPipeline(device, src->GetFormat(), depthStencilFormat,
/*sampleCount=*/dstTexture->GetSampleCount()));
Ref<BindGroupLayoutBase> bgl;
DAWN_TRY_ASSIGN(bgl, pipeline->GetBindGroupLayout(0));
Ref<BindGroupBase> bindGroup;
{
BindGroupEntry bgEntry = {};
bgEntry.binding = 0;
bgEntry.textureView = src;
BindGroupDescriptor bgDesc = {};
bgDesc.layout = bgl.Get();
bgDesc.entryCount = 1;
bgDesc.entries = &bgEntry;
DAWN_TRY_ASSIGN(bindGroup, device->CreateBindGroup(&bgDesc, UsageValidationMode::Internal));
}
// Draw to perform the blit.
renderEncoder->APISetBindGroup(0, bindGroup.Get(), 0, nullptr);
renderEncoder->APISetPipeline(pipeline.Get());
renderEncoder->APIDraw(3, 1, 0, 0);
return {};
}
size_t BlitColorToColorWithDrawPipelineKey::HashFunc::operator()(
const BlitColorToColorWithDrawPipelineKey& key) const {
size_t hash = 0;
HashCombine(&hash, key.colorFormat);
HashCombine(&hash, key.depthStencilFormat);
HashCombine(&hash, key.sampleCount);
return hash;
}
bool BlitColorToColorWithDrawPipelineKey::EqualityFunc::operator()(
const BlitColorToColorWithDrawPipelineKey& a,
const BlitColorToColorWithDrawPipelineKey& b) const {
return a.colorFormat == b.colorFormat && a.depthStencilFormat == b.depthStencilFormat &&
a.sampleCount == b.sampleCount;
}
} // namespace dawn::native