Improve Vulkan zeta surface texture correctness

This commit is contained in:
izzy2lost
2026-04-08 02:34:53 -04:00
parent d5374eb522
commit f6522b69ea
6 changed files with 175 additions and 15 deletions
+30
View File
@@ -1321,6 +1321,36 @@ static void begin_render_pass(PGRAPHState *pg)
assert(r->current_framebuffer != VK_NULL_HANDLE);
if (r->zeta_binding &&
r->zeta_binding->image_layout !=
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = r->zeta_binding->image_layout,
.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = r->zeta_binding->image,
.subresourceRange = {
.aspectMask = r->zeta_binding->host_fmt.aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
};
vkCmdPipelineBarrier(
r->command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
r->zeta_binding->image_layout =
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
VkRenderPassBeginInfo render_pass_begin_info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = r->render_pass,
+33
View File
@@ -202,6 +202,15 @@ void pgraph_vk_transition_image_layout(PGRAPHState *pg, VkCommandBuffer cmd,
sourceStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
// Depth Read-Only -> Src
} else if (oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
// Depth -> Dst
} else if (oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
@@ -210,6 +219,14 @@ void pgraph_vk_transition_image_layout(PGRAPHState *pg, VkCommandBuffer cmd,
sourceStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
// Depth Read-Only -> Dst
} else if (oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
// Src -> Color
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
@@ -226,6 +243,22 @@ void pgraph_vk_transition_image_layout(PGRAPHState *pg, VkCommandBuffer cmd,
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
// Src -> Depth Read-Only
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
// Dst -> Depth Read-Only
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
// Src -> Dst
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+2
View File
@@ -141,6 +141,7 @@ typedef struct SurfaceBinding {
VkImage image;
VkImageView image_view;
VkImageLayout image_layout;
VmaAllocation allocation;
// Used for scaling
@@ -449,6 +450,7 @@ typedef struct PGRAPHVkState {
TextureBinding *texture_bindings[NV2A_MAX_TEXTURES];
bool tex_surface_direct[NV2A_MAX_TEXTURES];
VkImageView tex_surface_direct_views[NV2A_MAX_TEXTURES];
VkImageLayout tex_surface_direct_layout[NV2A_MAX_TEXTURES];
TextureBinding dummy_texture;
bool texture_bindings_changed;
VkFormatProperties *texture_format_properties;
+1 -1
View File
@@ -217,7 +217,7 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
image_infos[i] = (VkDescriptorImageInfo){
.imageLayout = r->tex_surface_direct[i]
? VK_IMAGE_LAYOUT_GENERAL
? r->tex_surface_direct_layout[i]
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = r->tex_surface_direct[i]
? r->tex_surface_direct_views[i]
+16 -4
View File
@@ -343,9 +343,9 @@ static void download_surface_to_buffer(NV2AState *d, SurfaceBinding *surface,
pgraph_vk_transition_image_layout(
pg, cmd, surface->image, surface->host_fmt.vk_format,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
surface->image_layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
surface->image_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
int num_copy_regions = 1;
VkBufferImageCopy copy_regions[2];
@@ -455,6 +455,9 @@ static void download_surface_to_buffer(NV2AState *d, SurfaceBinding *surface,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
surface->image_layout =
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// FIXME: Verify output of depth stencil conversion
// FIXME: Track current layout and only transition when required
@@ -1145,6 +1148,9 @@ static void create_surface_image(PGRAPHState *pg, SurfaceBinding *surface)
VK_IMAGE_LAYOUT_UNDEFINED,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
surface->image_layout =
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
nv2a_profile_inc_counter(NV2A_PROF_QUEUE_SUBMIT_3);
pgraph_vk_end_debug_marker(r, cmd);
@@ -1156,6 +1162,7 @@ static void migrate_surface_image(SurfaceBinding *dst, SurfaceBinding *src)
{
dst->image = src->image;
dst->image_view = src->image_view;
dst->image_layout = src->image_layout;
dst->allocation = src->allocation;
dst->image_scratch = src->image_scratch;
dst->image_scratch_current_layout = src->image_scratch_current_layout;
@@ -1163,6 +1170,7 @@ static void migrate_surface_image(SurfaceBinding *dst, SurfaceBinding *src)
src->image = VK_NULL_HANDLE;
src->image_view = VK_NULL_HANDLE;
src->image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
src->allocation = VK_NULL_HANDLE;
src->image_scratch = VK_NULL_HANDLE;
src->image_scratch_current_layout = VK_IMAGE_LAYOUT_UNDEFINED;
@@ -1582,9 +1590,9 @@ void pgraph_vk_upload_surface_data(NV2AState *d, SurfaceBinding *surface,
pgraph_vk_transition_image_layout(
pg, cmd, surface->image, surface->host_fmt.vk_format,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
surface->image_layout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
surface->image_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
bool upscale = pg->surface_scale_factor > 1 &&
!use_compute_to_convert_depth_stencil_format;
@@ -1637,6 +1645,9 @@ void pgraph_vk_upload_surface_data(NV2AState *d, SurfaceBinding *surface,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
surface->image_layout =
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
nv2a_profile_inc_counter(NV2A_PROF_QUEUE_SUBMIT_2);
pgraph_vk_end_debug_marker(r, cmd);
@@ -1741,6 +1752,7 @@ static void populate_surface_binding_target_sized(NV2AState *d, bool color,
target->frame_time = pg->frame_time;
target->draw_time = pg->draw_time;
target->cleared = false;
target->image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
target->initialized = false;
}
+93 -10
View File
@@ -738,8 +738,9 @@ static void copy_zeta_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surfac
pgraph_vk_transition_image_layout(
pg, cmd, surface->image, surface->host_fmt.vk_format,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
surface->image_layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
surface->image_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
vkCmdCopyImageToBuffer(cmd, surface->image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@@ -765,6 +766,7 @@ static void copy_zeta_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surfac
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL,
0, NULL, 1, &depth_read_barrier);
surface->image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
VkImageViewCreateInfo depth_view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -852,6 +854,7 @@ static void copy_zeta_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surfac
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
0, 0, NULL, 0, NULL, 1, &depth_restore_barrier);
surface->image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
vkDestroyImageView(r->device, depth_view, NULL);
texture_source_buffer = r->storage_buffers[BUFFER_COMPUTE_SRC].buffer;
@@ -898,8 +901,9 @@ static void copy_zeta_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surfac
pgraph_vk_transition_image_layout(
pg, cmd, surface->image, surface->host_fmt.vk_format,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
surface->image_layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
surface->image_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
vkCmdCopyImageToBuffer(cmd, surface->image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@@ -910,6 +914,7 @@ static void copy_zeta_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surfac
pg, cmd, surface->image, surface->host_fmt.vk_format,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
surface->image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
size_t packed_image_size = scaled_width * scaled_height * 4;
@@ -1098,6 +1103,46 @@ static void bind_surface_as_texture(PGRAPHState *pg, SurfaceBinding *surface,
texture->draw_time = surface->draw_time;
}
static void bind_zeta_surface_as_texture(PGRAPHState *pg, SurfaceBinding *surface,
TextureBinding *texture)
{
assert(!surface->color);
assert(!(surface->host_fmt.aspect & VK_IMAGE_ASPECT_STENCIL_BIT));
nv2a_profile_inc_counter(NV2A_PROF_SURF_TO_TEX);
if (surface->image_layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) {
VkCommandBuffer cmd = pgraph_vk_begin_nondraw_commands(pg);
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = surface->image_layout,
.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = surface->image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
};
vkCmdPipelineBarrier(
cmd, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
pgraph_vk_end_nondraw_commands(pg, cmd);
surface->image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
texture->draw_time = surface->draw_time;
}
static void copy_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surface,
TextureBinding *texture)
{
@@ -1120,9 +1165,9 @@ static void copy_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surface,
pgraph_vk_transition_image_layout(
pg, cmd, surface->image, surface->host_fmt.vk_format,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
surface->image_layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
surface->image_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
pgraph_vk_transition_image_layout(pg, cmd, texture->image, vkf.vk_format,
texture->current_layout,
@@ -1149,6 +1194,9 @@ static void copy_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surface,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
surface->image_layout =
surface->color ? VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
pgraph_vk_transition_image_layout(pg, cmd, texture->image, vkf.vk_format,
texture->current_layout,
@@ -1413,6 +1461,8 @@ static void create_texture(PGRAPHState *pg, int texture_idx)
bool surface_to_texture = false;
r->tex_surface_direct[texture_idx] = false;
r->tex_surface_direct_views[texture_idx] = VK_NULL_HANDLE;
r->tex_surface_direct_layout[texture_idx] =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Check active surfaces to see if this texture was a render target
SurfaceBinding *surface = pgraph_vk_surface_get(d, texture_vram_offset);
@@ -1511,12 +1561,28 @@ static void create_texture(PGRAPHState *pg, int texture_idx)
if (binding_found) {
if (surface_to_texture) {
if (surface->color) {
if (surface->draw_time != snode->draw_time) {
bind_surface_as_texture(pg, surface, snode);
bool can_direct_bind =
surface->color ||
!(surface->host_fmt.aspect & VK_IMAGE_ASPECT_STENCIL_BIT);
if (can_direct_bind) {
VkImageLayout direct_layout;
if (surface->color) {
if (surface->draw_time != snode->draw_time) {
bind_surface_as_texture(pg, surface, snode);
}
direct_layout = VK_IMAGE_LAYOUT_GENERAL;
} else {
if (surface->draw_time != snode->draw_time ||
surface->image_layout !=
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) {
bind_zeta_surface_as_texture(pg, surface, snode);
}
direct_layout =
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
r->tex_surface_direct[texture_idx] = true;
r->tex_surface_direct_views[texture_idx] = surface->image_view;
r->tex_surface_direct_layout[texture_idx] = direct_layout;
} else if (surface->draw_time != snode->draw_time) {
copy_surface_to_texture(pg, surface, snode);
}
@@ -1734,10 +1800,21 @@ static void create_texture(PGRAPHState *pg, int texture_idx)
r->texture_bindings[texture_idx] = snode;
if (surface_to_texture) {
if (surface->color) {
bind_surface_as_texture(pg, surface, snode);
bool can_direct_bind =
surface->color ||
!(surface->host_fmt.aspect & VK_IMAGE_ASPECT_STENCIL_BIT);
if (can_direct_bind) {
VkImageLayout direct_layout;
if (surface->color) {
bind_surface_as_texture(pg, surface, snode);
direct_layout = VK_IMAGE_LAYOUT_GENERAL;
} else {
bind_zeta_surface_as_texture(pg, surface, snode);
direct_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
r->tex_surface_direct[texture_idx] = true;
r->tex_surface_direct_views[texture_idx] = surface->image_view;
r->tex_surface_direct_layout[texture_idx] = direct_layout;
} else {
copy_surface_to_texture(pg, surface, snode);
}
@@ -1820,6 +1897,8 @@ void pgraph_vk_bind_textures(NV2AState *d)
}
r->tex_surface_direct[i] = false;
r->tex_surface_direct_views[i] = VK_NULL_HANDLE;
r->tex_surface_direct_layout[i] =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
pg->texture_dirty[i] = false;
continue;
}
@@ -1834,12 +1913,14 @@ void pgraph_vk_bind_textures(NV2AState *d)
TextureBinding *prev_binding = r->texture_bindings[i];
bool prev_direct = r->tex_surface_direct[i];
VkImageView prev_direct_view = r->tex_surface_direct_views[i];
VkImageLayout prev_direct_layout = r->tex_surface_direct_layout[i];
create_texture(pg, i);
pg->texture_dirty[i] = false; // FIXME: Move to renderer?
if (r->texture_bindings[i] != prev_binding ||
r->tex_surface_direct[i] != prev_direct ||
r->tex_surface_direct_views[i] != prev_direct_view) {
r->tex_surface_direct_views[i] != prev_direct_view ||
r->tex_surface_direct_layout[i] != prev_direct_layout) {
r->texture_bindings_changed = true;
}
}
@@ -1976,6 +2057,8 @@ void pgraph_vk_finalize_textures(PGRAPHState *pg)
r->texture_bindings[i] = NULL;
r->tex_surface_direct[i] = false;
r->tex_surface_direct_views[i] = VK_NULL_HANDLE;
r->tex_surface_direct_layout[i] =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
destroy_dummy_texture(r);