mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
vulkan: present through an opaque shader pass instead of a blit
The plain present path copied the VRAM image into the swapchain with vkCmdBlitImage, alpha included. VRAM alpha is the PSX mask/STP bit, 0 on most pixels, and AMD's compositor honours it even with an OPAQUE compositeAlpha: parts of the window went see-through (8x8 black/white blocks of whatever sat behind it). Reported in #411. vk_renderer_present_vram/_wide now draw the displayed rect with a fullscreen triangle whose fragment shader writes alpha = 1 (new present.vert/.frag). The pass is self-contained and created lazily; if any of it fails to build, presents fall back to the old blit. Its render pass ends in TRANSFER_DST_OPTIMAL so finish_present (OSD + present barrier) is unchanged. Wide surfaces gain SAMPLED usage for it. Checked on an RX 7900 XT: intro/logo screens render correctly, no new validation-layer messages. Most of this was written by Claude Opus 5.5; I reviewed and tested it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
#version 450
|
||||
/* Swapchain present: sample the displayed VRAM rect and write it OPAQUE.
|
||||
* VRAM alpha carries the PSX mask/STP bit (0 on most pixels); copying it into
|
||||
* the swapchain with a transfer blit let AMD's compositor treat the window as
|
||||
* partly transparent (desktop showing through as black/white blocks). */
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 0) out vec4 o_col;
|
||||
layout(set = 0, binding = 0) uniform sampler2D u_src;
|
||||
layout(push_constant) uniform PC {
|
||||
vec4 u_src_rect; /* displayed region in normalized src coords: x0,y0,x1,y1 */
|
||||
} pc;
|
||||
void main() {
|
||||
o_col = vec4(texture(u_src, mix(pc.u_src_rect.xy, pc.u_src_rect.zw, v_uv)).rgb, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#version 450
|
||||
/* Fullscreen triangle for the swapchain present pass. The viewport is set to
|
||||
* the letterbox rect, so v_uv [0,1] spans exactly the displayed picture. */
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
void main() {
|
||||
vec2 p = vec2(float((gl_VertexIndex << 1) & 2), float(gl_VertexIndex & 2));
|
||||
v_uv = p;
|
||||
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
+253
-11
@@ -889,7 +889,11 @@ static int create_swapchain(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pp_release_swapchain(void);
|
||||
static void pp_destroy_all(void);
|
||||
|
||||
static void destroy_swapchain(void) {
|
||||
pp_release_swapchain(); /* present-pass views/framebuffers/pipeline */
|
||||
if (s_swapchain) { p_vkDestroySwapchainKHR(s_dev, s_swapchain, NULL); s_swapchain = VK_NULL_HANDLE; }
|
||||
}
|
||||
|
||||
@@ -1421,6 +1425,7 @@ static void osd_staging_free(void);
|
||||
void vk_renderer_shutdown(void) {
|
||||
if (!s_dev) return;
|
||||
p_vkDeviceWaitIdle(s_dev);
|
||||
pp_destroy_all();
|
||||
vk_gpu_sync_internal(); /* reclaim deferred staging before tearing down */
|
||||
cpres_cache_free(); /* FMV CPU-present cached image + staging */
|
||||
osd_staging_free(); /* host toast OSD staging */
|
||||
@@ -1679,6 +1684,219 @@ static void letterbox(int sw, int sh, int aw, int ah, VkOffset3D off[2]) {
|
||||
off[1].x = x + tw; off[1].y = y + th; off[1].z = 1;
|
||||
}
|
||||
|
||||
/* ---- opaque swapchain present pass --------------------------------------- */
|
||||
/* The plain present path blitted the VRAM image into the swapchain with
|
||||
* vkCmdBlitImage, which copies alpha too. VRAM alpha is the PSX mask/STP bit
|
||||
* (0 on most pixels), and AMD's compositor honours it despite
|
||||
* VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR: parts of the window turned transparent
|
||||
* (black/white blocks of whatever is behind it). This pass draws the displayed
|
||||
* rect with a fullscreen triangle whose fragment shader writes alpha = 1.
|
||||
* Everything is created lazily on first use; any failure sets s_pp_failed and
|
||||
* the callers fall back to the original blit. The render pass ends in
|
||||
* TRANSFER_DST_OPTIMAL so finish_present() (OSD blit + present barrier) runs
|
||||
* unchanged. Format-dependent objects are released with the swapchain. */
|
||||
static VkRenderPass s_pp_rp;
|
||||
static VkImageView s_pp_views[8];
|
||||
static VkFramebuffer s_pp_fbs[8];
|
||||
static VkPipeline s_pp_pipe;
|
||||
static VkDescriptorSetLayout s_pp_dsl;
|
||||
static VkPipelineLayout s_pp_pl;
|
||||
static VkDescriptorPool s_pp_pool;
|
||||
static VkDescriptorSet s_pp_ds[VK_FRAMES];
|
||||
static VkShaderModule s_pp_vs, s_pp_fs;
|
||||
static VkSampler s_pp_near, s_pp_lin;
|
||||
static int s_pp_failed;
|
||||
|
||||
typedef struct { float src_rect[4]; } PresentPush;
|
||||
|
||||
/* Swapchain-lifetime objects (the render pass depends on s_sc_format). */
|
||||
static void pp_release_swapchain(void) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (s_pp_fbs[i]) { p_vkDestroyFramebuffer(s_dev, s_pp_fbs[i], NULL); s_pp_fbs[i] = VK_NULL_HANDLE; }
|
||||
if (s_pp_views[i]) { p_vkDestroyImageView(s_dev, s_pp_views[i], NULL); s_pp_views[i] = VK_NULL_HANDLE; }
|
||||
}
|
||||
if (s_pp_pipe) { p_vkDestroyPipeline(s_dev, s_pp_pipe, NULL); s_pp_pipe = VK_NULL_HANDLE; }
|
||||
if (s_pp_rp) { p_vkDestroyRenderPass(s_dev, s_pp_rp, NULL); s_pp_rp = VK_NULL_HANDLE; }
|
||||
}
|
||||
|
||||
static void pp_destroy_all(void) {
|
||||
pp_release_swapchain();
|
||||
if (s_pp_pool) { p_vkDestroyDescriptorPool(s_dev, s_pp_pool, NULL); s_pp_pool = VK_NULL_HANDLE; }
|
||||
if (s_pp_pl) { p_vkDestroyPipelineLayout(s_dev, s_pp_pl, NULL); s_pp_pl = VK_NULL_HANDLE; }
|
||||
if (s_pp_dsl) { p_vkDestroyDescriptorSetLayout(s_dev, s_pp_dsl, NULL); s_pp_dsl = VK_NULL_HANDLE; }
|
||||
if (s_pp_vs) { p_vkDestroyShaderModule(s_dev, s_pp_vs, NULL); s_pp_vs = VK_NULL_HANDLE; }
|
||||
if (s_pp_fs) { p_vkDestroyShaderModule(s_dev, s_pp_fs, NULL); s_pp_fs = VK_NULL_HANDLE; }
|
||||
if (s_pp_near) { p_vkDestroySampler(s_dev, s_pp_near, NULL); s_pp_near = VK_NULL_HANDLE; }
|
||||
if (s_pp_lin) { p_vkDestroySampler(s_dev, s_pp_lin, NULL); s_pp_lin = VK_NULL_HANDLE; }
|
||||
s_pp_failed = 0;
|
||||
}
|
||||
|
||||
/* Device-lifetime objects: shaders, samplers, set layout, pool, sets, layout. */
|
||||
static int pp_init_device_objects(void) {
|
||||
if (s_pp_pl) return 1;
|
||||
s_pp_vs = make_module(spv_present_vert, spv_present_vert_size);
|
||||
s_pp_fs = make_module(spv_present_frag, spv_present_frag_size);
|
||||
if (!s_pp_vs || !s_pp_fs) return 0;
|
||||
|
||||
VkSamplerCreateInfo sci = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
sci.magFilter = sci.minFilter = VK_FILTER_NEAREST;
|
||||
sci.addressModeU = sci.addressModeV = sci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
sci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
if (p_vkCreateSampler(s_dev, &sci, NULL, &s_pp_near) != VK_SUCCESS) return 0;
|
||||
sci.magFilter = sci.minFilter = VK_FILTER_LINEAR;
|
||||
if (p_vkCreateSampler(s_dev, &sci, NULL, &s_pp_lin) != VK_SUCCESS) return 0;
|
||||
|
||||
VkDescriptorSetLayoutBinding b = {0};
|
||||
b.binding = 0; b.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
b.descriptorCount = 1; b.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
VkDescriptorSetLayoutCreateInfo dci = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
dci.bindingCount = 1; dci.pBindings = &b;
|
||||
if (p_vkCreateDescriptorSetLayout(s_dev, &dci, NULL, &s_pp_dsl) != VK_SUCCESS) return 0;
|
||||
|
||||
/* One set per in-flight frame: acquire_present waits the frame fence, so
|
||||
* the set is idle when it is rewritten for the next present. */
|
||||
VkDescriptorPoolSize ps = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_FRAMES };
|
||||
VkDescriptorPoolCreateInfo pci = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
pci.maxSets = VK_FRAMES; pci.poolSizeCount = 1; pci.pPoolSizes = &ps;
|
||||
if (p_vkCreateDescriptorPool(s_dev, &pci, NULL, &s_pp_pool) != VK_SUCCESS) return 0;
|
||||
for (int i = 0; i < VK_FRAMES; i++) {
|
||||
VkDescriptorSetAllocateInfo ai = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
ai.descriptorPool = s_pp_pool; ai.descriptorSetCount = 1; ai.pSetLayouts = &s_pp_dsl;
|
||||
if (p_vkAllocateDescriptorSets(s_dev, &ai, &s_pp_ds[i]) != VK_SUCCESS) return 0;
|
||||
}
|
||||
|
||||
VkPushConstantRange pr = { VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PresentPush) };
|
||||
VkPipelineLayoutCreateInfo li = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
li.setLayoutCount = 1; li.pSetLayouts = &s_pp_dsl;
|
||||
li.pushConstantRangeCount = 1; li.pPushConstantRanges = ≺
|
||||
return p_vkCreatePipelineLayout(s_dev, &li, NULL, &s_pp_pl) == VK_SUCCESS;
|
||||
}
|
||||
|
||||
static int pp_init_swapchain_objects(void) {
|
||||
if (s_pp_pipe) return 1;
|
||||
VkAttachmentDescription att = {0};
|
||||
att.format = s_sc_format; att.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
att.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; /* black letterbox bars */
|
||||
att.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
att.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
att.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
att.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
att.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; /* finish_present */
|
||||
VkAttachmentReference cref = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
VkSubpassDescription sub = {0};
|
||||
sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
sub.colorAttachmentCount = 1; sub.pColorAttachments = &cref;
|
||||
VkSubpassDependency dep[2] = {0};
|
||||
dep[0].srcSubpass = VK_SUBPASS_EXTERNAL; dep[0].dstSubpass = 0;
|
||||
dep[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
/* The OSD blit in finish_present writes the image with transfer ops. */
|
||||
dep[1].srcSubpass = 0; dep[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dep[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dep[1].dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
dep[1].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
VkRenderPassCreateInfo rpi = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rpi.attachmentCount = 1; rpi.pAttachments = &att;
|
||||
rpi.subpassCount = 1; rpi.pSubpasses = ⊂
|
||||
rpi.dependencyCount = 2; rpi.pDependencies = dep;
|
||||
if (p_vkCreateRenderPass(s_dev, &rpi, NULL, &s_pp_rp) != VK_SUCCESS) return 0;
|
||||
|
||||
VkPipelineShaderStageCreateInfo st[2] = {
|
||||
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO },
|
||||
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO } };
|
||||
st[0].stage = VK_SHADER_STAGE_VERTEX_BIT; st[0].module = s_pp_vs; st[0].pName = "main";
|
||||
st[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; st[1].module = s_pp_fs; st[1].pName = "main";
|
||||
VkPipelineVertexInputStateCreateInfo vin = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
VkPipelineInputAssemblyStateCreateInfo ia = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
VkPipelineViewportStateCreateInfo vp = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
vp.viewportCount = 1; vp.scissorCount = 1;
|
||||
VkPipelineRasterizationStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.lineWidth = 1.0f;
|
||||
VkPipelineMultisampleStateCreateInfo ms = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
VkPipelineColorBlendAttachmentState ba = {0};
|
||||
ba.colorWriteMask = 0xF;
|
||||
VkPipelineColorBlendStateCreateInfo cb = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
||||
cb.attachmentCount = 1; cb.pAttachments = &ba;
|
||||
VkDynamicState dyn[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||
VkPipelineDynamicStateCreateInfo dy = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
dy.dynamicStateCount = 2; dy.pDynamicStates = dyn;
|
||||
VkGraphicsPipelineCreateInfo ci = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
||||
ci.stageCount = 2; ci.pStages = st;
|
||||
ci.pVertexInputState = &vin; ci.pInputAssemblyState = &ia;
|
||||
ci.pViewportState = &vp; ci.pRasterizationState = &rs;
|
||||
ci.pMultisampleState = &ms; ci.pColorBlendState = &cb; ci.pDynamicState = &dy;
|
||||
ci.layout = s_pp_pl; ci.renderPass = s_pp_rp;
|
||||
return p_vkCreateGraphicsPipelines(s_dev, VK_NULL_HANDLE, 1, &ci, NULL, &s_pp_pipe) == VK_SUCCESS;
|
||||
}
|
||||
|
||||
/* Framebuffer for swapchain image idx, created on first use. */
|
||||
static VkFramebuffer pp_framebuffer(uint32_t idx) {
|
||||
if (idx >= 8 || idx >= s_sc_count) return VK_NULL_HANDLE;
|
||||
if (s_pp_fbs[idx]) return s_pp_fbs[idx];
|
||||
VkImageViewCreateInfo vi = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
vi.image = s_sc_images[idx]; vi.viewType = VK_IMAGE_VIEW_TYPE_2D; vi.format = s_sc_format;
|
||||
vi.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
vi.subresourceRange.levelCount = 1; vi.subresourceRange.layerCount = 1;
|
||||
if (p_vkCreateImageView(s_dev, &vi, NULL, &s_pp_views[idx]) != VK_SUCCESS) {
|
||||
s_pp_views[idx] = VK_NULL_HANDLE; return VK_NULL_HANDLE;
|
||||
}
|
||||
VkFramebufferCreateInfo fi = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
fi.renderPass = s_pp_rp; fi.attachmentCount = 1; fi.pAttachments = &s_pp_views[idx];
|
||||
fi.width = s_sc_extent.width; fi.height = s_sc_extent.height; fi.layers = 1;
|
||||
if (p_vkCreateFramebuffer(s_dev, &fi, NULL, &s_pp_fbs[idx]) != VK_SUCCESS)
|
||||
s_pp_fbs[idx] = VK_NULL_HANDLE;
|
||||
return s_pp_fbs[idx];
|
||||
}
|
||||
|
||||
/* Record the opaque present of src_view (already SHADER_READ_ONLY_OPTIMAL,
|
||||
* normalized src rect u0..v1) into the letterbox rect dst of the acquired
|
||||
* swapchain image, leaving it in TRANSFER_DST_OPTIMAL. Returns 0 (and records
|
||||
* nothing) when the pass is unavailable: the caller then uses the blit. */
|
||||
static int pp_draw(VkCommandBuffer cb, uint32_t idx, uint32_t fr, VkImageView src_view,
|
||||
int linear, const VkOffset3D dst[2],
|
||||
float u0, float v0, float u1, float v1) {
|
||||
if (s_pp_failed) return 0;
|
||||
if (!pp_init_device_objects() || !pp_init_swapchain_objects()) {
|
||||
s_pp_failed = 1;
|
||||
fprintf(stdout, "psxrecomp: vulkan: present pass unavailable, using blit\n");
|
||||
return 0;
|
||||
}
|
||||
VkFramebuffer fb = pp_framebuffer(idx);
|
||||
if (!fb) return 0;
|
||||
|
||||
VkDescriptorImageInfo ii = { linear ? s_pp_lin : s_pp_near, src_view,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
|
||||
VkWriteDescriptorSet w = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
|
||||
w.dstSet = s_pp_ds[fr]; w.dstBinding = 0; w.descriptorCount = 1;
|
||||
w.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; w.pImageInfo = ⅈ
|
||||
p_vkUpdateDescriptorSets(s_dev, 1, &w, 0, NULL);
|
||||
|
||||
VkClearValue clear = {0};
|
||||
clear.color.float32[3] = 1.0f;
|
||||
VkRenderPassBeginInfo bi = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
|
||||
bi.renderPass = s_pp_rp; bi.framebuffer = fb;
|
||||
bi.renderArea.extent = s_sc_extent;
|
||||
bi.clearValueCount = 1; bi.pClearValues = &clear;
|
||||
p_vkCmdBeginRenderPass(cb, &bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||
VkViewport view = { (float)dst[0].x, (float)dst[0].y,
|
||||
(float)(dst[1].x - dst[0].x), (float)(dst[1].y - dst[0].y), 0.0f, 1.0f };
|
||||
VkRect2D sci = { { dst[0].x, dst[0].y },
|
||||
{ (uint32_t)(dst[1].x - dst[0].x), (uint32_t)(dst[1].y - dst[0].y) } };
|
||||
p_vkCmdSetViewport(cb, 0, 1, &view);
|
||||
p_vkCmdSetScissor(cb, 0, 1, &sci);
|
||||
p_vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, s_pp_pipe);
|
||||
p_vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, s_pp_pl, 0, 1, &s_pp_ds[fr], 0, NULL);
|
||||
PresentPush pc = { { u0, v0, u1, v1 } };
|
||||
p_vkCmdPushConstants(cb, s_pp_pl, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof pc, &pc);
|
||||
p_vkCmdDraw(cb, 3, 1, 0, 0);
|
||||
p_vkCmdEndRenderPass(cb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vk_renderer_present_vram(int disp_x, int disp_y, int w, int h,
|
||||
int linear, int force_4_3) {
|
||||
if (!s_ctx_ok) return 0;
|
||||
@@ -1688,6 +1906,22 @@ int vk_renderer_present_vram(int disp_x, int disp_y, int w, int h,
|
||||
if (!acquire_present(&sc, &cb, &idx, &fr)) return 1; /* frame skipped/recreated */
|
||||
|
||||
int S = s_scale;
|
||||
VkOffset3D dst[2];
|
||||
letterbox((int)s_sc_extent.width, (int)s_sc_extent.height,
|
||||
force_4_3 ? 4 : s_aspect_num, force_4_3 ? 3 : s_aspect_den, dst);
|
||||
|
||||
/* Opaque shader present first (see pp_draw); blit only as the fallback. */
|
||||
vram_to(cb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
int drawn = pp_draw(cb, idx, fr, s_vram_view, linear, dst,
|
||||
disp_x / (float)VRAM_W, disp_y / (float)VRAM_H,
|
||||
(disp_x + w) / (float)VRAM_W, (disp_y + h) / (float)VRAM_H);
|
||||
vram_to(cb, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); /* re-park */
|
||||
if (drawn) {
|
||||
finish_present(cb, sc, idx, fr);
|
||||
perf_snapshot_present();
|
||||
return 1;
|
||||
}
|
||||
|
||||
img_barrier(cb, sc, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
@@ -1696,10 +1930,6 @@ int vk_renderer_present_vram(int disp_x, int disp_y, int w, int h,
|
||||
VkImageSubresourceRange rng = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
p_vkCmdClearColorImage(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &rng);
|
||||
|
||||
VkOffset3D dst[2];
|
||||
letterbox((int)s_sc_extent.width, (int)s_sc_extent.height,
|
||||
force_4_3 ? 4 : s_aspect_num, force_4_3 ? 3 : s_aspect_den, dst);
|
||||
|
||||
VkImageBlit blit = {0};
|
||||
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.srcSubresource.layerCount = 1;
|
||||
@@ -1850,6 +2080,24 @@ int vk_renderer_present_wide(int disp_x, int disp_y, int disp_h, int linear) {
|
||||
if (!acquire_present(&sc, &cb, &idx, &fr)) return 1; /* frame skipped/recreated */
|
||||
|
||||
int S = s_scale;
|
||||
int native_w = s_wide_w - 2 * s_wide_offset;
|
||||
if (native_w <= 0) native_w = s_wide_w;
|
||||
VkOffset3D dst[2];
|
||||
letterbox((int)s_sc_extent.width, (int)s_sc_extent.height,
|
||||
4 * s_wide_w, 3 * native_w, dst);
|
||||
|
||||
/* Opaque shader present first (see pp_draw); blit only as the fallback. */
|
||||
img_to(cb, s_wide_img[i], &s_wide_layout[i], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
int drawn = pp_draw(cb, idx, fr, s_wide_view[i], linear, dst,
|
||||
0.0f, disp_y / (float)VRAM_H,
|
||||
1.0f, (disp_y + disp_h) / (float)VRAM_H);
|
||||
if (drawn) {
|
||||
img_to(cb, s_wide_img[i], &s_wide_layout[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
finish_present(cb, sc, idx, fr);
|
||||
perf_snapshot_present();
|
||||
return 1;
|
||||
}
|
||||
|
||||
img_barrier(cb, sc, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
@@ -1859,12 +2107,6 @@ int vk_renderer_present_wide(int disp_x, int disp_y, int disp_h, int linear) {
|
||||
|
||||
img_to(cb, s_wide_img[i], &s_wide_layout[i], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
int native_w = s_wide_w - 2 * s_wide_offset;
|
||||
if (native_w <= 0) native_w = s_wide_w;
|
||||
VkOffset3D dst[2];
|
||||
letterbox((int)s_sc_extent.width, (int)s_sc_extent.height,
|
||||
4 * s_wide_w, 3 * native_w, dst);
|
||||
|
||||
int sy0 = disp_y * S, sy1 = (disp_y + disp_h) * S;
|
||||
if (sy0 < 0) sy0 = 0;
|
||||
if (sy1 > VRAM_H * S) sy1 = VRAM_H * S;
|
||||
@@ -2565,7 +2807,7 @@ static int wide_surf_for(int base_x) {
|
||||
int S = s_scale, w = s_wide_w * S, h = VRAM_H * S;
|
||||
if (!make_image(VK_FORMAT_R8G8B8A8_UNORM, w, h,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, &s_wide_img[i], &s_wide_mem[i], &s_wide_view[i]))
|
||||
return -1;
|
||||
if (!make_image(s_ds_format, w, h,
|
||||
|
||||
Reference in New Issue
Block a user