diff --git a/hw/xbox/nv2a/pgraph/vk/draw.c b/hw/xbox/nv2a/pgraph/vk/draw.c index 440ec7ffd4..6dc4a16ebb 100644 --- a/hw/xbox/nv2a/pgraph/vk/draw.c +++ b/hw/xbox/nv2a/pgraph/vk/draw.c @@ -1243,6 +1243,36 @@ static void begin_render_pass(PGRAPHState *pg) assert(r->framebuffer_index > 0); + 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, diff --git a/hw/xbox/nv2a/pgraph/vk/image.c b/hw/xbox/nv2a/pgraph/vk/image.c index e2dec87f59..1e2211a88e 100644 --- a/hw/xbox/nv2a/pgraph/vk/image.c +++ b/hw/xbox/nv2a/pgraph/vk/image.c @@ -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) { diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index f0c3634ab5..41d2cb55a5 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -136,6 +136,7 @@ typedef struct SurfaceBinding { VkImage image; VkImageView image_view; + VkImageLayout image_layout; VmaAllocation allocation; // Used for scaling @@ -402,6 +403,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; diff --git a/hw/xbox/nv2a/pgraph/vk/shaders.c b/hw/xbox/nv2a/pgraph/vk/shaders.c index d783c69204..5279d2b08d 100644 --- a/hw/xbox/nv2a/pgraph/vk/shaders.c +++ b/hw/xbox/nv2a/pgraph/vk/shaders.c @@ -209,7 +209,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] diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index e6466adb49..d427f183da 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -207,9 +207,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]; @@ -319,6 +319,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 @@ -889,6 +892,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); @@ -900,6 +906,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; @@ -907,6 +914,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; @@ -1286,9 +1294,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; @@ -1341,6 +1349,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); @@ -1443,6 +1454,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; } diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 68a8ec3e45..741ff1dce9 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -756,19 +756,15 @@ 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, dst_storage_buffer->buffer, num_regions, regions); - pgraph_vk_transition_image_layout( - pg, cmd, surface->image, surface->host_fmt.vk_format, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - VkBuffer texture_source_buffer; if (use_compute_to_convert_depth_stencil) { @@ -887,6 +883,12 @@ static void copy_zeta_surface_to_texture(PGRAPHState *pg, SurfaceBinding *surfac VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); texture->current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + pgraph_vk_transition_image_layout( + pg, cmd, surface->image, surface->host_fmt.vk_format, + surface->image_layout, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + surface->image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + pgraph_vk_end_debug_marker(r, cmd); pgraph_vk_end_nondraw_commands(pg, cmd); @@ -976,6 +978,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) { @@ -998,9 +1040,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, @@ -1027,6 +1069,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, @@ -1291,6 +1336,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); @@ -1399,12 +1446,29 @@ static void create_texture(PGRAPHState *pg, int texture_idx) r->tex_surface_direct[texture_idx] = true; r->tex_surface_direct_views[texture_idx] = get_surface_direct_texture_view(r, surface, snode); + r->tex_surface_direct_layout[texture_idx] = VK_IMAGE_LAYOUT_GENERAL; } else { 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; + } + } else { + bool can_direct_bind = + !(surface->host_fmt.aspect & VK_IMAGE_ASPECT_STENCIL_BIT); + if (can_direct_bind) { + 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); + } + r->tex_surface_direct[texture_idx] = true; + r->tex_surface_direct_views[texture_idx] = surface->image_view; + r->tex_surface_direct_layout[texture_idx] = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + } else if (surface->draw_time != snode->draw_time) { + copy_surface_to_texture(pg, surface, snode); } - } else if (surface->draw_time != snode->draw_time) { - copy_surface_to_texture(pg, surface, snode); } } else { if (possibly_dirty && content_hash != snode->hash) { @@ -1619,12 +1683,25 @@ static void create_texture(PGRAPHState *pg, int texture_idx) r->tex_surface_direct[texture_idx] = true; r->tex_surface_direct_views[texture_idx] = get_surface_direct_texture_view(r, surface, snode); + r->tex_surface_direct_layout[texture_idx] = VK_IMAGE_LAYOUT_GENERAL; } else { 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; } } else { - copy_surface_to_texture(pg, surface, snode); + bool can_direct_bind = + !(surface->host_fmt.aspect & VK_IMAGE_ASPECT_STENCIL_BIT); + if (can_direct_bind) { + bind_zeta_surface_as_texture(pg, surface, snode); + r->tex_surface_direct[texture_idx] = true; + r->tex_surface_direct_views[texture_idx] = surface->image_view; + r->tex_surface_direct_layout[texture_idx] = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + } else { + copy_surface_to_texture(pg, surface, snode); + } } } else { upload_texture_image(pg, texture_idx, snode); @@ -1687,6 +1764,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; } @@ -1701,12 +1780,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; } } @@ -1845,6 +1926,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);