blob: 6f4cf7c3e728a24666c6eceabada6fb0bdc55047 [file] [log] [blame]
// Copyright 2024 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/vulkan/ResolveTextureLoadingUtilsVk.h"
#include <sstream>
#include <string>
#include <utility>
#include "absl/container/inlined_vector.h"
#include "dawn/common/Assert.h"
#include "dawn/common/Enumerator.h"
#include "dawn/native/BindGroup.h"
#include "dawn/native/Commands.h"
#include "dawn/native/Device.h"
#include "dawn/native/InternalPipelineStore.h"
#include "dawn/native/utils/WGPUHelpers.h"
#include "dawn/native/vulkan/BindGroupLayoutVk.h"
#include "dawn/native/vulkan/BindGroupVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/PipelineLayoutVk.h"
#include "dawn/native/vulkan/RenderPipelineVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
#include "dawn/native/webgpu_absl_format.h"
namespace dawn::native::vulkan {
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);
}
)";
std::string GenerateFS(const BlitColorToColorWithDrawPipelineKey& pipelineKey) {
std::ostringstream outputStructStream;
std::ostringstream assignOutputsStream;
std::ostringstream finalStream;
finalStream << "enable chromium_internal_input_attachments;";
for (auto i : IterateBitSet(pipelineKey.attachmentsToExpandResolve)) {
finalStream << absl::StrFormat(
"@group(0) @binding(%u) @input_attachment_index(%u) var srcTex%u : "
"input_attachment<f32>;\n",
i, i, i);
outputStructStream << absl::StrFormat("@location(%u) output%u : vec4f,\n", i, i);
assignOutputsStream << absl::StrFormat(
"\toutputColor.output%u = inputAttachmentLoad(srcTex%u);\n", i, i);
}
finalStream << "struct OutputColor {\n" << outputStructStream.str() << "}\n\n";
finalStream << R"(
@fragment fn blit_to_color() -> OutputColor {
var outputColor : OutputColor;
)" << assignOutputsStream.str()
<< R"(
return outputColor;
})";
return finalStream.str();
}
ResultOrError<Ref<RenderPipelineBase>> GetOrCreateColorBlitPipeline(
DeviceBase* device,
const BlitColorToColorWithDrawPipelineKey& pipelineKey,
uint8_t colorAttachmentCount) {
InternalPipelineStore* store = device->GetInternalPipelineStore();
{
auto it = store->expandResolveTexturePipelines.find(pipelineKey);
if (it != store->expandResolveTexturePipelines.end()) {
return it->second;
}
}
// vertex shader.
Ref<ShaderModuleBase> vshaderModule;
DAWN_TRY_ASSIGN(vshaderModule, utils::CreateShaderModule(device, kBlitToColorVS));
// fragment shader's source will depend on pipeline key.
std::string fsCode = GenerateFS(pipelineKey);
Ref<ShaderModuleBase> fshaderModule;
DAWN_TRY_ASSIGN(fshaderModule, utils::CreateShaderModule(
device, fsCode.c_str(),
{tint::wgsl::Extension::kChromiumInternalInputAttachments}));
FragmentState fragmentState = {};
fragmentState.module = fshaderModule.Get();
// Color target states.
PerColorAttachment<ColorTargetState> colorTargets = {};
PerColorAttachment<wgpu::ColorTargetStateExpandResolveTextureDawn> msaaExpandResolveStates{};
for (auto [i, target] : Enumerate(colorTargets)) {
target.format = pipelineKey.colorTargetFormats[i];
// We shouldn't change the color targets that are not involved in.
if (pipelineKey.resolveTargetsMask[i]) {
target.nextInChain = &msaaExpandResolveStates[i];
msaaExpandResolveStates[i].enabled = pipelineKey.attachmentsToExpandResolve[i];
if (msaaExpandResolveStates[i].enabled) {
target.writeMask = wgpu::ColorWriteMask::All;
} else {
target.writeMask = wgpu::ColorWriteMask::None;
}
} else {
target.writeMask = wgpu::ColorWriteMask::None;
}
}
fragmentState.targetCount = colorAttachmentCount;
fragmentState.targets = colorTargets.data();
RenderPipelineDescriptor renderPipelineDesc = {};
renderPipelineDesc.label = "blit_color_to_color";
renderPipelineDesc.vertex.module = vshaderModule.Get();
renderPipelineDesc.fragment = &fragmentState;
// Depth stencil state.
DepthStencilState depthStencilState = {};
if (pipelineKey.depthStencilFormat != wgpu::TextureFormat::Undefined) {
depthStencilState.format = pipelineKey.depthStencilFormat;
renderPipelineDesc.depthStencil = &depthStencilState;
}
// Multisample state.
DAWN_ASSERT(pipelineKey.sampleCount > 1);
renderPipelineDesc.multisample.count = pipelineKey.sampleCount;
renderPipelineDesc.layout = nullptr;
Ref<RenderPipelineBase> pipeline;
DAWN_TRY_ASSIGN(
pipeline, device->CreateRenderPipeline(&renderPipelineDesc, /*allowInternalBinding=*/true));
store->expandResolveTexturePipelines.emplace(pipelineKey, pipeline);
return pipeline;
}
} // namespace
MaybeError BeginRenderPassAndExpandResolveTextureWithDraw(Device* device,
CommandRecordingContext* commandContext,
const BeginRenderPassCmd* renderPass,
const VkRenderPassBeginInfo& beginInfo) {
DAWN_ASSERT(device->IsLockedByCurrentThreadIfNeeded());
// Construct pipeline key
BlitColorToColorWithDrawPipelineKey pipelineKey;
ColorAttachmentIndex colorAttachmentCount =
GetHighestBitIndexPlusOne(renderPass->attachmentState->GetColorAttachmentsMask());
for (ColorAttachmentIndex colorIdx :
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
const auto& colorAttachment = renderPass->colorAttachments[colorIdx];
const auto& view = colorAttachment.view;
DAWN_ASSERT(view != nullptr);
const Format& format = view->GetFormat();
TextureComponentType baseType = format.GetAspectInfo(Aspect::Color).baseType;
// Blitting integer textures are not currently supported.
DAWN_ASSERT(baseType == TextureComponentType::Float);
if (colorAttachment.loadOp == wgpu::LoadOp::ExpandResolveTexture) {
// TODO(42240662): Handle the cases where resolveTarget is altered by workarounds such
// as ResolveMultipleAttachmentInSeparatePasses/AlwaysResolveIntoZeroLevelAndLayer. We
// need to careful handle such cases because the render pass' compatibility could be
// affected as well.
DAWN_INVALID_IF(colorAttachment.resolveTarget == nullptr,
"resolveTarget at %d has been removed by some workarounds. %s doesn't "
"support this yet.",
colorIdx, colorAttachment.loadOp);
DAWN_ASSERT(colorAttachment.resolveTarget->GetLayerCount() == 1u);
DAWN_ASSERT(colorAttachment.resolveTarget->GetDimension() ==
wgpu::TextureViewDimension::e2D);
pipelineKey.attachmentsToExpandResolve.set(colorIdx);
}
pipelineKey.resolveTargetsMask.set(colorIdx, colorAttachment.resolveTarget != nullptr);
pipelineKey.colorTargetFormats[colorIdx] = format.format;
pipelineKey.sampleCount = view->GetTexture()->GetSampleCount();
}
DAWN_ASSERT(pipelineKey.attachmentsToExpandResolve.any());
pipelineKey.depthStencilFormat = wgpu::TextureFormat::Undefined;
if (renderPass->depthStencilAttachment.view != nullptr) {
pipelineKey.depthStencilFormat =
renderPass->depthStencilAttachment.view->GetFormat().format;
}
Ref<RenderPipelineBase> pipeline;
DAWN_TRY_ASSIGN(pipeline, GetOrCreateColorBlitPipeline(
device, pipelineKey, static_cast<uint8_t>(colorAttachmentCount)));
RenderPipeline* pipelineVk = ToBackend(pipeline.Get());
PipelineLayout* layoutVk = ToBackend(pipeline->GetLayout());
DAWN_ASSERT(layoutVk != nullptr);
// Construct bind group.
Ref<BindGroupLayoutBase> bgl;
DAWN_TRY_ASSIGN(bgl, pipelineVk->GetBindGroupLayout(0));
Ref<BindGroupBase> bindGroup;
absl::InlinedVector<BindGroupEntry, kMaxColorAttachments> bgEntries = {};
for (auto colorIdx : IterateBitSet(pipelineKey.attachmentsToExpandResolve)) {
const auto& colorAttachment = renderPass->colorAttachments[colorIdx];
bgEntries.push_back({});
auto& bgEntry = bgEntries.back();
bgEntry.binding = static_cast<uint8_t>(colorIdx);
bgEntry.textureView = colorAttachment.resolveTarget.Get();
// Transition the resolve texture
auto* textureVk = static_cast<Texture*>(colorAttachment.resolveTarget->GetTexture());
textureVk->TransitionUsageNow(commandContext, kResolveAttachmentLoadingUsage,
wgpu::ShaderStage::Fragment,
colorAttachment.resolveTarget->GetSubresourceRange());
}
BindGroupDescriptor bgDesc = {};
bgDesc.layout = bgl.Get();
bgDesc.entryCount = bgEntries.size();
bgDesc.entries = bgEntries.data();
DAWN_TRY_ASSIGN(bindGroup, device->CreateBindGroup(&bgDesc, UsageValidationMode::Internal));
BindGroup* bindGroupVk = ToBackend(bindGroup.Get());
// Start the render pass
VkCommandBuffer commandBuffer = commandContext->commandBuffer;
device->fn.CmdBeginRenderPass(commandBuffer, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
// Draw to perform the blit.
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(renderPass->width);
viewport.height = static_cast<float>(renderPass->height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
device->fn.CmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent.width = renderPass->width;
scissor.extent.height = renderPass->height;
device->fn.CmdSetScissor(commandBuffer, 0, 1, &scissor);
device->fn.CmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
*pipelineVk->GetHandle());
device->fn.CmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
*layoutVk->GetHandle(), 0, 1, &*bindGroupVk->GetHandle(), 0,
nullptr);
device->fn.CmdDraw(commandBuffer, 3, 1, 0, 0);
device->fn.CmdNextSubpass(commandBuffer, VK_SUBPASS_CONTENTS_INLINE);
// Subpass dependency automatically transitions the layouts of the resolve textures
// to RenderAttachment. So we need to notify TextureVk and don't need to use any explicit
// barriers.
for (auto colorIdx : IterateBitSet(pipelineKey.attachmentsToExpandResolve)) {
const auto& colorAttachment = renderPass->colorAttachments[colorIdx];
auto* textureVk = static_cast<Texture*>(colorAttachment.resolveTarget->GetTexture());
textureVk->UpdateUsage(wgpu::TextureUsage::RenderAttachment, wgpu::ShaderStage::Fragment,
colorAttachment.resolveTarget->GetSubresourceRange());
}
return {};
}
} // namespace dawn::native::vulkan