mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
Bind only exported color targets and preserve MRT slots
Related work: https://github.com/KytyPS5/KytyPS5/pull/558 Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com>
This commit is contained in:
@@ -666,13 +666,16 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
|||||||
key.ps_shader_id = ps_id;
|
key.ps_shader_id = ps_id;
|
||||||
auto& static_params = key.static_params;
|
auto& static_params = key.static_params;
|
||||||
auto& rendering = key.rendering;
|
auto& rendering = key.rendering;
|
||||||
rendering.color_count = color_count;
|
rendering.color_count = 0;
|
||||||
uint32_t attachment_samples = 0;
|
uint32_t attachment_samples = 0;
|
||||||
for (uint32_t i = 0; i < color_count; i++) {
|
for (uint32_t i = 0; i < color_count; i++) {
|
||||||
|
const auto slot = colors[i].target_slot;
|
||||||
|
EXIT_IF(slot >= RENDER_COLOR_ATTACHMENTS_MAX);
|
||||||
|
rendering.color_count = std::max(rendering.color_count, slot + 1);
|
||||||
EXIT_IF(!colors[i].image_id || colors[i].desc.view_info.format == vk::Format::eUndefined);
|
EXIT_IF(!colors[i].image_id || colors[i].desc.view_info.format == vk::Format::eUndefined);
|
||||||
static_params.color_mask[i] = colors[i].export_mapping.ApplyMask(
|
static_params.color_mask[slot] = colors[i].export_mapping.ApplyMask(
|
||||||
render_target_mask_slot(ctx.GetRenderTargetMask(), colors[i].target_slot));
|
render_target_mask_slot(ctx.GetRenderTargetMask(), colors[i].target_slot));
|
||||||
rendering.color_formats[i] = colors[i].desc.view_info.format;
|
rendering.color_formats[slot] = colors[i].desc.view_info.format;
|
||||||
if (attachment_samples == 0) {
|
if (attachment_samples == 0) {
|
||||||
attachment_samples = colors[i].desc.info.samples;
|
attachment_samples = colors[i].desc.info.samples;
|
||||||
} else if (attachment_samples != colors[i].desc.info.samples) {
|
} else if (attachment_samples != colors[i].desc.info.samples) {
|
||||||
@@ -681,14 +684,14 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
|||||||
}
|
}
|
||||||
const auto& rt = ctx.GetRenderTarget(colors[i].target_slot);
|
const auto& rt = ctx.GetRenderTarget(colors[i].target_slot);
|
||||||
const auto& bc = ctx.GetBlendControl(colors[i].target_slot);
|
const auto& bc = ctx.GetBlendControl(colors[i].target_slot);
|
||||||
static_params.color_srcblend[i] = bc.color_srcblend;
|
static_params.color_srcblend[slot] = bc.color_srcblend;
|
||||||
static_params.color_comb_fcn[i] = bc.color_comb_fcn;
|
static_params.color_comb_fcn[slot] = bc.color_comb_fcn;
|
||||||
static_params.color_destblend[i] = bc.color_destblend;
|
static_params.color_destblend[slot] = bc.color_destblend;
|
||||||
static_params.alpha_srcblend[i] = bc.alpha_srcblend;
|
static_params.alpha_srcblend[slot] = bc.alpha_srcblend;
|
||||||
static_params.alpha_comb_fcn[i] = bc.alpha_comb_fcn;
|
static_params.alpha_comb_fcn[slot] = bc.alpha_comb_fcn;
|
||||||
static_params.alpha_destblend[i] = bc.alpha_destblend;
|
static_params.alpha_destblend[slot] = bc.alpha_destblend;
|
||||||
static_params.separate_alpha_blend[i] = bc.separate_alpha_blend;
|
static_params.separate_alpha_blend[slot] = bc.separate_alpha_blend;
|
||||||
static_params.blend_enable[i] = bc.enable && !rt.info.blend_bypass;
|
static_params.blend_enable[slot] = bc.enable && !rt.info.blend_bypass;
|
||||||
}
|
}
|
||||||
const bool with_depth =
|
const bool with_depth =
|
||||||
depth.desc.view_info.format != vk::Format::eUndefined && static_cast<bool>(depth.image_id);
|
depth.desc.view_info.format != vk::Format::eUndefined && static_cast<bool>(depth.image_id);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||||
|
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
@@ -308,24 +309,12 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo
|
|||||||
|
|
||||||
static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
||||||
const ShaderVertexInputInfo& vs_input_info,
|
const ShaderVertexInputInfo& vs_input_info,
|
||||||
const RenderColorInfo* colors, uint32_t color_count,
|
const RenderDepthInfo& depth, const RenderState& rendering) {
|
||||||
const RenderDepthInfo& depth) {
|
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
|
|
||||||
EXIT_IF(colors == nullptr);
|
|
||||||
const auto& ctx = buffer.GetRegisters();
|
const auto& ctx = buffer.GetRegisters();
|
||||||
|
const auto& vp = ctx.GetScreenViewport();
|
||||||
const auto& vp = ctx.GetScreenViewport();
|
const vk::Extent2D framebuffer_extent {rendering.width, rendering.height};
|
||||||
vk::Extent2D framebuffer_extent {};
|
|
||||||
if (color_count > 0 && colors[0].image_id) {
|
|
||||||
framebuffer_extent = colors[0].Extent();
|
|
||||||
} else if (depth.image_id) {
|
|
||||||
framebuffer_extent = {depth.desc.info.extent.width, depth.desc.info.extent.height};
|
|
||||||
} else {
|
|
||||||
const auto& limits = buffer.GetGraphics().GetPhysicalDeviceProperties().limits;
|
|
||||||
framebuffer_extent = {limits.maxFramebufferWidth, limits.maxFramebufferHeight};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& outputs = vs_input_info.stage.program->info.outputs;
|
const auto& outputs = vs_input_info.stage.program->info.outputs;
|
||||||
const bool indexed_viewports =
|
const bool indexed_viewports =
|
||||||
std::any_of(outputs.begin(), outputs.end(), [](const auto& output) {
|
std::any_of(outputs.begin(), outputs.end(), [](const auto& output) {
|
||||||
@@ -420,15 +409,11 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
|
|||||||
// eColorWriteEnableEXT dynamic state and relies on the static colorWriteMask instead.
|
// eColorWriteEnableEXT dynamic state and relies on the static colorWriteMask instead.
|
||||||
#else
|
#else
|
||||||
vk::Bool32 enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
vk::Bool32 enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||||
// Color-control operation selects special color-buffer paths, not the normal component write
|
for (uint32_t slot = 0; slot < rendering.num_color_attachments; slot++) {
|
||||||
// mask. Attachment availability therefore follows the target write mask.
|
enable[slot] = rendering.color_attachments[slot].image_view != nullptr;
|
||||||
for (uint32_t i = 0; i < color_count; i++) {
|
|
||||||
enable[i] = render_target_mask_slot(ctx.GetRenderTargetMask(), colors[i].target_slot) != 0
|
|
||||||
? VK_TRUE
|
|
||||||
: VK_FALSE;
|
|
||||||
}
|
}
|
||||||
if (color_count != 0) {
|
if (rendering.num_color_attachments != 0) {
|
||||||
vk_buffer.setColorWriteEnableEXT(color_count, enable);
|
vk_buffer.setColorWriteEnableEXT(rendering.num_color_attachments, enable);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -474,7 +459,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
|||||||
state.width = std::numeric_limits<uint32_t>::max();
|
state.width = std::numeric_limits<uint32_t>::max();
|
||||||
state.height = std::numeric_limits<uint32_t>::max();
|
state.height = std::numeric_limits<uint32_t>::max();
|
||||||
state.num_layers = std::numeric_limits<uint32_t>::max();
|
state.num_layers = std::numeric_limits<uint32_t>::max();
|
||||||
state.num_color_attachments = color_count;
|
state.num_color_attachments = 0;
|
||||||
uint32_t attachment_samples = 0;
|
uint32_t attachment_samples = 0;
|
||||||
for (uint32_t i = 0; i < color_count; i++) {
|
for (uint32_t i = 0; i < color_count; i++) {
|
||||||
auto& target = colors[i];
|
auto& target = colors[i];
|
||||||
@@ -516,7 +501,8 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
|||||||
state.width = std::min(state.width, extent.width);
|
state.width = std::min(state.width, extent.width);
|
||||||
state.height = std::min(state.height, extent.height);
|
state.height = std::min(state.height, extent.height);
|
||||||
state.num_layers = std::min(state.num_layers, view.layer_count);
|
state.num_layers = std::min(state.num_layers, view.layer_count);
|
||||||
auto& attachment = state.color_attachments[i];
|
state.num_color_attachments = std::max(state.num_color_attachments, target.target_slot + 1);
|
||||||
|
auto& attachment = state.color_attachments[target.target_slot];
|
||||||
attachment.image_view = image_view;
|
attachment.image_view = image_view;
|
||||||
attachment.image_layout = layout;
|
attachment.image_layout = layout;
|
||||||
}
|
}
|
||||||
@@ -891,42 +877,6 @@ static bool ResolvePrimitiveRestart(const CommandBuffer& buffer,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw,
|
|
||||||
uint32_t render_target_slice_offset,
|
|
||||||
DrawRenderState& state) {
|
|
||||||
auto& ctx = buffer.GetRegisters();
|
|
||||||
|
|
||||||
if (ResolveColorTargets(buffer, render_target_slice_offset)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (draw.IsIndexed()) {
|
|
||||||
LogDrawPhase(draw.Name(), "ResolveRenderColorTarget");
|
|
||||||
}
|
|
||||||
for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) {
|
|
||||||
if (slot == 0 || (render_target_mask_slot(ctx.GetRenderTargetMask(), slot) != 0 &&
|
|
||||||
ctx.GetRenderTarget(slot).base.addr != 0)) {
|
|
||||||
ResolveRenderColorTarget(buffer, state.color_info[state.color_count],
|
|
||||||
render_target_slice_offset, slot);
|
|
||||||
if (state.color_info[state.color_count].image_id) {
|
|
||||||
state.color_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (draw.IsIndexed()) {
|
|
||||||
LogDrawPhase(draw.Name(), "ResolveRenderDepthTarget");
|
|
||||||
}
|
|
||||||
ResolveRenderDepthTarget(buffer, state.depth_info);
|
|
||||||
|
|
||||||
state.ps_active = DrawHasActivePixelShader(buffer);
|
|
||||||
if (state.color_count == 0 && !state.depth_info.image_id && !state.ps_active) {
|
|
||||||
LogFramebufferSkip(draw.Name(), state.color_info[0], state.depth_info, buffer,
|
|
||||||
draw.index_count, 0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw,
|
static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw,
|
||||||
DrawRenderState& state) {
|
DrawRenderState& state) {
|
||||||
auto& ctx = buffer.GetRegisters();
|
auto& ctx = buffer.GetRegisters();
|
||||||
@@ -940,8 +890,14 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw,
|
|||||||
state.ps_input_info = {};
|
state.ps_input_info = {};
|
||||||
std::array<Prospero::ColorComponentMapping, RENDER_COLOR_ATTACHMENTS_MAX>
|
std::array<Prospero::ColorComponentMapping, RENDER_COLOR_ATTACHMENTS_MAX>
|
||||||
target_export_mapping {};
|
target_export_mapping {};
|
||||||
for (uint32_t i = 0; i < state.color_count; i++) {
|
for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) {
|
||||||
target_export_mapping[state.color_info[i].target_slot] = state.color_info[i].export_mapping;
|
const auto& rt = ctx.GetRenderTarget(slot);
|
||||||
|
if (rt.base.addr != 0 && render_target_mask_slot(ctx.GetRenderTargetMask(), slot) != 0) {
|
||||||
|
target_export_mapping[slot] =
|
||||||
|
TextureGetRenderTargetFormat(rt.info.format, rt.info.channel_type,
|
||||||
|
rt.info.channel_order)
|
||||||
|
.export_mapping;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto& pipeline_cache = buffer.GetContext().GetPipelineCache();
|
auto& pipeline_cache = buffer.GetContext().GetPipelineCache();
|
||||||
if (draw.IsIndexed()) {
|
if (draw.IsIndexed()) {
|
||||||
@@ -952,6 +908,45 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw,
|
|||||||
target_export_mapping, state.ps_active, state.vertex_info, state.ps_input_info);
|
target_export_mapping, state.ps_active, state.vertex_info, state.ps_input_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw,
|
||||||
|
uint32_t render_target_slice_offset,
|
||||||
|
DrawRenderState& state) {
|
||||||
|
state.ps_active = DrawHasActivePixelShader(buffer);
|
||||||
|
RefreshShaders(buffer, draw, state);
|
||||||
|
uint32_t mrt_mask = 0;
|
||||||
|
if (state.ps_active) {
|
||||||
|
for (const auto& output: state.ps_input_info.stage.program->info.outputs) {
|
||||||
|
if (output.kind == ShaderRecompiler::IR::StageOutputKind::Mrt) {
|
||||||
|
mrt_mask |= 1u << output.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (draw.IsIndexed()) {
|
||||||
|
LogDrawPhase(draw.Name(), "ResolveRenderColorTarget");
|
||||||
|
}
|
||||||
|
for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) {
|
||||||
|
if ((mrt_mask & (1u << slot)) != 0) {
|
||||||
|
ResolveRenderColorTarget(buffer, state.color_info[state.color_count],
|
||||||
|
render_target_slice_offset, slot);
|
||||||
|
if (state.color_info[state.color_count].image_id) {
|
||||||
|
state.color_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (draw.IsIndexed()) {
|
||||||
|
LogDrawPhase(draw.Name(), "ResolveRenderDepthTarget");
|
||||||
|
}
|
||||||
|
ResolveRenderDepthTarget(buffer, state.depth_info);
|
||||||
|
|
||||||
|
if (state.color_count == 0 && !state.depth_info.image_id && !state.ps_active) {
|
||||||
|
LogFramebufferSkip(draw.Name(), state.color_info[0], state.depth_info, buffer,
|
||||||
|
draw.index_count, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static PreparedIndexBuffer PrepareIndexBuffer(CommandBuffer& buffer,
|
static PreparedIndexBuffer PrepareIndexBuffer(CommandBuffer& buffer,
|
||||||
const DrawIndexBufferSource& source) {
|
const DrawIndexBufferSource& source) {
|
||||||
PreparedIndexBuffer prepared;
|
PreparedIndexBuffer prepared;
|
||||||
@@ -1160,8 +1155,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
|||||||
CommitIndexBuffer(vk_buffer, index_binding);
|
CommitIndexBuffer(vk_buffer, index_binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetGraphicsDynamicParams(buffer, vk_buffer, vertex_stages.back(), state.color_info,
|
SetGraphicsDynamicParams(buffer, vk_buffer, vertex_stages.back(), state.depth_info, rendering);
|
||||||
state.color_count, state.depth_info);
|
|
||||||
if (m_context.GetGraphics().attachment_feedback_loop_enabled) {
|
if (m_context.GetGraphics().attachment_feedback_loop_enabled) {
|
||||||
vk_buffer.setAttachmentFeedbackLoopEnableEXT(
|
vk_buffer.setAttachmentFeedbackLoopEnableEXT(
|
||||||
rendering.depth_stencil_attachment.image_layout ==
|
rendering.depth_stencil_attachment.image_layout ==
|
||||||
@@ -1226,7 +1220,8 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer)) {
|
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer) ||
|
||||||
|
ResolveColorTargets(buffer, args.render_target_slice_offset)) {
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1300,8 +1295,6 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshShaders(buffer, draw, state);
|
|
||||||
|
|
||||||
LogDrawStateIfNeeded(buffer, draw, state, args.index_type_and_size,
|
LogDrawStateIfNeeded(buffer, draw, state, args.index_type_and_size,
|
||||||
args.index_addr);
|
args.index_addr);
|
||||||
|
|
||||||
@@ -1338,7 +1331,8 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer)) {
|
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer) ||
|
||||||
|
ResolveColorTargets(buffer, args.render_target_slice_offset)) {
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1367,18 +1361,16 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
|||||||
const DrawCallInfo draw {CommandBufferDebugOp::DrawIndexAuto,
|
const DrawCallInfo draw {CommandBufferDebugOp::DrawIndexAuto,
|
||||||
args.vertex_count, args.instance_count, args.first_instance};
|
args.vertex_count, args.instance_count, args.first_instance};
|
||||||
|
|
||||||
DrawRenderState state {};
|
|
||||||
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) {
|
|
||||||
ResetBindings();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
if (!GetDrawTopology(ucfg, true, topology)) {
|
if (!GetDrawTopology(ucfg, true, topology)) {
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RefreshShaders(buffer, draw, state);
|
DrawRenderState state {};
|
||||||
|
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) {
|
||||||
|
ResetBindings();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const bool rect_list = ucfg.GetPrimType() == Prospero::PrimitiveType::kRectList;
|
const bool rect_list = ucfg.GetPrimType() == Prospero::PrimitiveType::kRectList;
|
||||||
if (rect_list && state.vertex_info[0].buffers_num == 0 &&
|
if (rect_list && state.vertex_info[0].buffers_num == 0 &&
|
||||||
|
|||||||
@@ -379,6 +379,11 @@ struct TextureCacheTestAccess {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RenderExecutorTestAccess {
|
struct RenderExecutorTestAccess {
|
||||||
|
static void DrawAuto(RenderExecutor &executor, CommandBuffer &command,
|
||||||
|
const DrawAutoArgs &args) {
|
||||||
|
executor.DrawAuto(0, command, args);
|
||||||
|
}
|
||||||
|
|
||||||
static bool TryConsumeComputeImageClear(RenderExecutor &executor,
|
static bool TryConsumeComputeImageClear(RenderExecutor &executor,
|
||||||
const ShaderComputeInputInfo &input, CommandBuffer &command,
|
const ShaderComputeInputInfo &input, CommandBuffer &command,
|
||||||
uint32_t x, uint32_t y, uint32_t z, uint32_t mode) {
|
uint32_t x, uint32_t y, uint32_t z, uint32_t mode) {
|
||||||
@@ -13095,8 +13100,6 @@ public:
|
|||||||
check_stencil("masked compare passes", false, 0xb8, 0xb9);
|
check_stencil("masked compare passes", false, 0xb8, 0xb9);
|
||||||
check_stencil("masked compare fails", false, 0xb0, 0xb0);
|
check_stencil("masked compare fails", false, 0xb0, 0xb0);
|
||||||
|
|
||||||
DestroyBuffer(&stencil_readback);
|
|
||||||
|
|
||||||
// SPI_PS_IN_CONTROL must select the native wave width through the actual
|
// SPI_PS_IN_CONTROL must select the native wave width through the actual
|
||||||
// program cache. In wave32 a low-word compare preserves scalar VCC_HI.
|
// program cache. In wave32 a low-word compare preserves scalar VCC_HI.
|
||||||
static const auto native_vertex = [] {
|
static const auto native_vertex = [] {
|
||||||
@@ -13248,6 +13251,117 @@ public:
|
|||||||
"zero positions drew an extra triangle, or a valid position was culled");
|
"zero positions drew an extra triangle, or a valid position was culled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A NULL-export stencil pass must ignore smaller stale color targets,
|
||||||
|
// materialize the whole pending HTile clear, and still execute pixel discard.
|
||||||
|
static const auto null_pixels = [] {
|
||||||
|
std::array<std::vector<u32>, 2> result;
|
||||||
|
for (uint32_t discard = 0; discard < result.size(); discard++) {
|
||||||
|
auto &code = result[discard];
|
||||||
|
if (discard != 0) {
|
||||||
|
code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||||
|
}
|
||||||
|
code.push_back(EncodeExp0(0x09, 0, true, false, true));
|
||||||
|
code.push_back(EncodeExp1(0, 0, 0, 0));
|
||||||
|
AppendEnd(&code);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
registers.Reset();
|
||||||
|
registers.SetViewportTransformControl(0x300);
|
||||||
|
registers.SetViewportScaleOffset(0, extent / 2, extent / 2,
|
||||||
|
extent / 2, extent / 2, 1, 0);
|
||||||
|
registers.SetViewportZMax(0, 1);
|
||||||
|
registers.SetScreenScissor(0, 0, extent, extent);
|
||||||
|
registers.SetWindowScissor(0, 0, extent, extent, false);
|
||||||
|
registers.SetGenericScissor(0, 0, extent, extent, false);
|
||||||
|
registers.SetViewportScissor(0, 0, 0, extent, extent, false);
|
||||||
|
registers.SetRenderTargetMask(0xf00f);
|
||||||
|
registers.SetShaderMask(0xf00f);
|
||||||
|
registers.SetPsInControl(0x8000);
|
||||||
|
registers.SetDepthShaderControl({.shader_kill_enable = true});
|
||||||
|
for (const auto slot : {0u, 3u}) {
|
||||||
|
registers.SetColorBase(slot,
|
||||||
|
{.addr = depth_address + (slot == 0 ? 0x38000 : 0x10000)});
|
||||||
|
registers.SetColorInfo(slot,
|
||||||
|
{.format = Prospero::ChannelLayout::k32_32_32_32,
|
||||||
|
.channel_type = Prospero::ChannelType::kFloat,
|
||||||
|
.channel_order = Prospero::ChannelOrder::kStandard});
|
||||||
|
const auto side = slot == 0 ? extent / 4 : extent;
|
||||||
|
registers.SetColorAttrib2(slot, {.height = side - 1, .width = side - 1});
|
||||||
|
registers.SetColorAttrib3(slot,
|
||||||
|
{.tile_mode = Prospero::TileMode::kLinear, .dimension = 1});
|
||||||
|
registers.SetTargetOutputMode(slot, 4);
|
||||||
|
}
|
||||||
|
stencil_target.z_info.htile_acceleration = true;
|
||||||
|
stencil_target.htile_data_base_addr = depth_address + 0x30000;
|
||||||
|
registers.SetDepthRenderTarget(stencil_target);
|
||||||
|
registers.SetDepthClearValue(1);
|
||||||
|
stencil_control = {};
|
||||||
|
stencil_control.stencil_enable = true;
|
||||||
|
stencil_control.stencilfunc = static_cast<uint8_t>(vk::CompareOp::eAlways);
|
||||||
|
registers.SetDepthControl(stencil_control);
|
||||||
|
registers.SetStencilControl({3, 3, 3, 3, 3, 3});
|
||||||
|
registers.SetStencilMask({0x80, 0, 0x80, 1, 0x80, 0, 0x80, 1});
|
||||||
|
user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriList);
|
||||||
|
shaders.SetEsShaderBase(vertex_address);
|
||||||
|
for (uint32_t discard = 0; discard < null_pixels.size(); discard++) {
|
||||||
|
const auto &code = null_pixels[discard];
|
||||||
|
const auto address = reinterpret_cast<uint64_t>(code.data());
|
||||||
|
ShaderMapUserData(address,
|
||||||
|
{.type = Prospero::ShaderBinaryType::kPs,
|
||||||
|
.code_size_bytes = static_cast<uint32_t>(code.size() * sizeof(u32))});
|
||||||
|
shaders.SetPsShaderBase(address);
|
||||||
|
RenderExecutorTestAccess::ResolveRenderDepthTarget(executor, scheduler.Current(), depth);
|
||||||
|
(void)cache.FindDepthTarget(depth.image_id, depth.desc);
|
||||||
|
vk::ClearValue zero{};
|
||||||
|
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), depth.image_id,
|
||||||
|
{vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
|
||||||
|
0, 1, 0, 1}, zero);
|
||||||
|
Require(name, "pending stencil-pass HTile clear",
|
||||||
|
cache.ClearMeta(stencil_target.htile_data_base_addr),
|
||||||
|
"the stencil pass did not retain its HTile metadata");
|
||||||
|
RenderExecutorTestAccess::DrawAuto(
|
||||||
|
executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1});
|
||||||
|
const auto depth_pixels = ReadCachedTexel(name, context, depth.image_id,
|
||||||
|
{}, {extent, extent, 1});
|
||||||
|
Require(name, "NULL-export full depth clear",
|
||||||
|
std::ranges::all_of(depth_pixels, [](u32 v) { return v == 0x3f800000u; }),
|
||||||
|
"an unexported color target clipped the pending depth clear");
|
||||||
|
const auto stencil = read_stencil();
|
||||||
|
const auto expected = discard == 0 ? 0x80808080u : 0u;
|
||||||
|
Require(name, "NULL-export stencil coverage and discard",
|
||||||
|
std::ranges::all_of(stencil, [=](u32 v) { return v == expected; }),
|
||||||
|
"a stale color target clipped stencil coverage or the NULL-export shader was skipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
// With MRT0 still bound, an MRT3-only export must retain location3 in
|
||||||
|
// rendering attachments, pipeline formats, blend masks and dynamic write enables.
|
||||||
|
static const auto sparse_pixel = [] {
|
||||||
|
auto code = native_pixel;
|
||||||
|
*std::ranges::find(code, EncodeExp0(0x00, 0xf)) = EncodeExp0(0x03, 0xf);
|
||||||
|
return code;
|
||||||
|
}();
|
||||||
|
const auto sparse_address = reinterpret_cast<uint64_t>(sparse_pixel.data());
|
||||||
|
ShaderMapUserData(sparse_address,
|
||||||
|
{.type = Prospero::ShaderBinaryType::kPs,
|
||||||
|
.code_size_bytes = static_cast<uint32_t>(sparse_pixel.size() * sizeof(u32))});
|
||||||
|
shaders.SetPsShaderBase(sparse_address);
|
||||||
|
registers.SetDepthControl({});
|
||||||
|
registers.SetDepthShaderControl({});
|
||||||
|
RenderExecutorTestAccess::DrawAuto(
|
||||||
|
executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1});
|
||||||
|
RenderColorInfo sparse_color{};
|
||||||
|
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||||
|
executor, scheduler.Current(), sparse_color, 3);
|
||||||
|
const auto sparse_pixels = ReadCachedTexel(name, context, sparse_color.image_id,
|
||||||
|
{}, {extent, extent, 1});
|
||||||
|
for (size_t component = 0; component < sparse_pixels.size(); component++) {
|
||||||
|
const auto expected = component % 4 == 3 ? 0x3f800000u : 0x3e800000u;
|
||||||
|
Require(name, "sparse MRT3 output", sparse_pixels[component] == expected,
|
||||||
|
"MRT3 was compacted to a different slot or clipped by unused MRT0");
|
||||||
|
}
|
||||||
|
DestroyBuffer(&stencil_readback);
|
||||||
|
|
||||||
vertex_shader = owned_vertex_shader;
|
vertex_shader = owned_vertex_shader;
|
||||||
pixel_shader = owned_pixel_shader;
|
pixel_shader = owned_pixel_shader;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user