renderer_vulkan: Surface fills and dual screen rendering working

This commit is contained in:
emufan
2022-06-16 11:51:23 +03:00
parent d4b88ac158
commit 0eff9ad215
10 changed files with 211 additions and 126 deletions

View File

@ -39,6 +39,8 @@ struct Rectangle {
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
}
auto operator <=> (const Rectangle<T>& other) const = default;
};
template <typename T>

View File

@ -280,7 +280,7 @@ void RendererVulkan::CreateVulkanObjects() {
// Generate VBO handle for drawing
VKBuffer::Info vertex_info{
.size = sizeof(ScreenRectVertex) * 4,
.size = sizeof(ScreenRectVertex) * 10,
.properties = vk::MemoryPropertyFlagBits::eDeviceLocal,
.usage = vk::BufferUsageFlagBits::eVertexBuffer |
vk::BufferUsageFlagBits::eTransferDst
@ -340,19 +340,23 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
* rotation.
*/
void RendererVulkan::DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y,
float w, float h) {
void RendererVulkan::DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h) {
auto& screen_info = screen_infos[screen_id];
const auto& texcoords = screen_info.display_texcoords;
u32 size = sizeof(ScreenRectVertex) * 4;
auto [ptr, offset, invalidate] = vertex_buffer.Map(size);
const std::array vertices{
ScreenRectVertex(x, y, texcoords.bottom, texcoords.left),
ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right),
ScreenRectVertex(x, y + h, texcoords.top, texcoords.left),
ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right),
ScreenRectVertex(x, y, texcoords.bottom, texcoords.left, screen_id),
ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right, screen_id),
ScreenRectVertex(x, y + h, texcoords.top, texcoords.left, screen_id),
ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right, screen_id),
};
auto data = std::as_bytes(std::span(vertices));
vertex_buffer.Upload(data, 0);
std::memcpy(ptr, vertices.data(), size);
vertex_buffer.Commit(size, vk::AccessFlagBits::eVertexAttributeRead,
vk::PipelineStageFlagBits::eVertexInput);
// As this is the "DrawSingleScreenRotated" function, the output resolution dimensions have been
// swapped. If a non-rotated draw-screen function were to be added for book-mode games, those
@ -365,34 +369,32 @@ void RendererVulkan::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa
1.0f / (height * scale_factor)};
draw_info.o_resolution = glm::vec4{h, w, 1.0f / h, 1.0f / w};
auto& image = swapchain->GetCurrentImage();
auto& state = VulkanState::Get();
state.BeginRendering(image, std::nullopt, false, clear_color, vk::AttachmentLoadOp::eClear);
state.SetPresentData(draw_info);
state.SetPresentTexture(*screen_info.display_texture);
state.ApplyPresentState();
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
vk::DeviceSize offset = 0;
cmdbuffer.bindVertexBuffers(0, 1, &vertex_buffer.GetBuffer(), &offset);
cmdbuffer.draw(4, 1, 0, 0);
cmdbuffer.bindVertexBuffers(0, vertex_buffer.GetBuffer(), {0});
cmdbuffer.draw(4, 1, offset / sizeof(ScreenRectVertex), 0);
}
void RendererVulkan::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w,
float h) {
void RendererVulkan::DrawSingleScreen(u32 screen_id, float x, float y, float w, float h) {
auto& screen_info = screen_infos[screen_id];
const auto& texcoords = screen_info.display_texcoords;
u32 size = sizeof(ScreenRectVertex) * 4;
auto [ptr, offset, invalidate] = vertex_buffer.Map(size);
const std::array vertices{
ScreenRectVertex(x, y, texcoords.bottom, texcoords.right),
ScreenRectVertex(x + w, y, texcoords.top, texcoords.right),
ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left),
ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left),
ScreenRectVertex(x, y, texcoords.bottom, texcoords.right, screen_id),
ScreenRectVertex(x + w, y, texcoords.top, texcoords.right, screen_id),
ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left, screen_id),
ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left, screen_id),
};
auto data = std::as_bytes(std::span(vertices));
vertex_buffer.Upload(data, 0);
std::memcpy(ptr, vertices.data(), size);
vertex_buffer.Commit(size, vk::AccessFlagBits::eVertexAttributeRead,
vk::PipelineStageFlagBits::eVertexInput);
const u16 scale_factor = VideoCore::GetResolutionScaleFactor();
auto [width, height] = screen_info.texture.GetArea().extent;
@ -403,17 +405,13 @@ void RendererVulkan::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl
1.0f / (height * scale_factor)};
draw_info.o_resolution = glm::vec4{h, w, 1.0f / h, 1.0f / w};
auto& image = swapchain->GetCurrentImage();
auto& state = VulkanState::Get();
state.BeginRendering(image, std::nullopt, false, clear_color, vk::AttachmentLoadOp::eClear);
state.SetPresentData(draw_info);
state.SetPresentTexture(*screen_info.display_texture);
state.ApplyPresentState();
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
cmdbuffer.bindVertexBuffers(0, vertex_buffer.GetBuffer(), {0});
cmdbuffer.draw(4, 1, 0, 0);
cmdbuffer.draw(4, 1, offset / sizeof(ScreenRectVertex), 0);
}
/**
@ -423,7 +421,8 @@ void RendererVulkan::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl
void RendererVulkan::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l,
const ScreenInfo& screen_info_r, float x,
float y, float w, float h) {
DrawSingleScreenRotated(screen_info_l, x, y, w, h);
ASSERT(false);
//DrawSingleScreenRotated(screen_info_l, x, y, w, h);
/*const auto& texcoords = screen_info_l.display_texcoords;
const std::array<ScreenRectVertex, 4> vertices = {{
@ -460,7 +459,8 @@ void RendererVulkan::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info
void RendererVulkan::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
const ScreenInfo& screen_info_r, float x, float y,
float w, float h) {
DrawSingleScreen(screen_info_l, x, y, w, h);
ASSERT(false);
//DrawSingleScreen(screen_info_l, x, y, w, h);
/*const auto& texcoords = screen_info_l.display_texcoords;
const std::array<ScreenRectVertex, 4> vertices = {{
@ -518,8 +518,9 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
const auto& bottom_screen = layout.bottom_screen;
// Set projection matrix
draw_info.modelview = MakeOrthographicMatrix((float)layout.width, (float)layout.height, flipped);
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
static_cast<float>(layout.height), 0.0f,
0.f, 1.f));
const bool stereo_single_screen = false
/* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
@ -530,28 +531,37 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
//glUniform1i(uniform_color_texture_r, 1);
}
auto& image = swapchain->GetCurrentImage();
auto& state = VulkanState::Get();
state.BeginRendering(image, std::nullopt, false, clear_color, vk::AttachmentLoadOp::eClear);
state.SetPresentTextures(screen_infos[0].display_texture->GetView(),
screen_infos[1].display_texture->GetView(),
screen_infos[2].display_texture->GetView());
state.ApplyPresentState();
draw_info.layer = 0;
if (layout.top_screen_enabled) {
if (layout.is_rotated) {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left,
(float)top_screen.top, (float)top_screen.GetWidth(),
(float)top_screen.GetHeight());
DrawSingleScreenRotated(0, top_screen.left,
top_screen.top, top_screen.GetWidth(),
top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
DrawSingleScreenRotated(0, (float)top_screen.left / 2,
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
(float)top_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreenRotated(screen_infos[1],
DrawSingleScreenRotated(1,
((float)top_screen.left / 2) + ((float)layout.width / 2),
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
(float)top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreenRotated(screen_infos[0], layout.top_screen.left,
DrawSingleScreenRotated(0, layout.top_screen.left,
layout.top_screen.top, layout.top_screen.GetWidth(),
layout.top_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreenRotated(screen_infos[1],
DrawSingleScreenRotated(1,
layout.cardboard.top_screen_right_eye +
((float)layout.width / 2),
layout.top_screen.top, layout.top_screen.GetWidth(),
@ -563,21 +573,21 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
}
} else {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreen(screen_infos[0], (float)top_screen.left, (float)top_screen.top,
DrawSingleScreen(0, (float)top_screen.left, (float)top_screen.top,
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreen(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top,
DrawSingleScreen(0, (float)top_screen.left / 2, (float)top_screen.top,
(float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreen(screen_infos[1],
DrawSingleScreen(1,
((float)top_screen.left / 2) + ((float)layout.width / 2),
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
(float)top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreen(screen_infos[0], layout.top_screen.left, layout.top_screen.top,
DrawSingleScreen(0, layout.top_screen.left, layout.top_screen.top,
layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreen(screen_infos[1],
DrawSingleScreen(1,
layout.cardboard.top_screen_right_eye + ((float)layout.width / 2),
layout.top_screen.top, layout.top_screen.GetWidth(),
layout.top_screen.GetHeight());
@ -588,28 +598,29 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
}
}
}
draw_info.layer = 0;
if (/*layout.bottom_screen_enabled*/false) {
if (layout.bottom_screen_enabled) {
if (layout.is_rotated) {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
DrawSingleScreenRotated(2, (float)bottom_screen.left,
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreenRotated(
screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top,
2, (float)bottom_screen.left / 2, (float)bottom_screen.top,
(float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreenRotated(
screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
2, ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreenRotated(screen_infos[2], layout.bottom_screen.left,
DrawSingleScreenRotated(2, layout.bottom_screen.left,
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
layout.bottom_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreenRotated(screen_infos[2],
DrawSingleScreenRotated(2,
layout.cardboard.bottom_screen_right_eye +
((float)layout.width / 2),
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
@ -622,24 +633,24 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
}
} else {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreen(screen_infos[2], (float)bottom_screen.left,
DrawSingleScreen(2, (float)bottom_screen.left,
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreen(screen_infos[2], (float)bottom_screen.left / 2,
DrawSingleScreen(2, (float)bottom_screen.left / 2,
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreen(screen_infos[2],
DrawSingleScreen(2,
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreen(screen_infos[2], layout.bottom_screen.left,
DrawSingleScreen(2, layout.bottom_screen.left,
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
layout.bottom_screen.GetHeight());
draw_info.layer = 1;
DrawSingleScreen(screen_infos[2],
DrawSingleScreen(2,
layout.cardboard.bottom_screen_right_eye +
((float)layout.width / 2),
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),

View File

@ -52,8 +52,8 @@ private:
void ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer);
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h);
void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h);
void DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h);
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h);
void DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l,
const ScreenInfo& screen_info_r, float x, float y, float w, float h);
void DrawSingleScreenStereo(const ScreenInfo& screen_info_l, const ScreenInfo& screen_info_r,
@ -70,7 +70,7 @@ private:
private:
// Vulkan state
DrawInfo draw_info{};
VKBuffer vertex_buffer;
StreamBuffer vertex_buffer;
vk::ClearColorValue clear_color{};
/// Display information for top and bottom screens respectively

View File

@ -133,7 +133,7 @@ std::tuple<u8*, u32, bool> StreamBuffer::Map(u32 size, u32 alignment) {
auto [staging_ptr, staging_offset] = g_vk_task_scheduler->RequestStaging(size);
mapped_chunk = vk::BufferCopy{staging_offset, buffer_pos, size};
return std::make_tuple(staging_ptr + buffer_pos, buffer_pos, invalidate);
return std::make_tuple(staging_ptr, buffer_pos, invalidate);
}
void StreamBuffer::Commit(u32 size, vk::AccessFlags access_to_block,
@ -148,7 +148,7 @@ void StreamBuffer::Commit(u32 size, vk::AccessFlags access_to_block,
vk::BufferMemoryBarrier barrier{
vk::AccessFlagBits::eTransferWrite, access_to_block,
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
buffer, mapped_chunk.srcOffset, mapped_chunk.size
buffer, mapped_chunk.dstOffset, mapped_chunk.size
};
// Add a pipeline barrier for the region modified
@ -158,9 +158,6 @@ void StreamBuffer::Commit(u32 size, vk::AccessFlags access_to_block,
buffer_pos += size;
}
else {
printf("f");
}
}
}

View File

@ -288,6 +288,55 @@ static bool BlitTextures(const Surface& src_surface, const Common::Rectangle<u32
return true;
}
static bool FillSurface(const Surface& surface, std::array<u8, 4> fill_buffer,
Common::Rectangle<u32> rect) {
if (surface->GetScaledRect() != rect) {
// TODO: use vkCmdClearAttachments to clear subrects
LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented");
}
vk::ImageSubresourceRange image_range{{}, 0, 1, 0, 1};
switch (surface->type) {
case SurfaceParams::SurfaceType::Color:
case SurfaceParams::SurfaceType::Texture:
image_range.aspectMask = vk::ImageAspectFlagBits::eColor;
break;
case SurfaceParams::SurfaceType::Depth:
image_range.aspectMask = vk::ImageAspectFlagBits::eDepth;
break;
case SurfaceParams::SurfaceType::DepthStencil:
image_range.aspectMask =
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
break;
default:
UNIMPLEMENTED();
}
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
switch (surface->type) {
case SurfaceParams::SurfaceType::Color:
case SurfaceParams::SurfaceType::Texture: {
Pica::Texture::TextureInfo tex_info{};
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface->pixel_format);
auto color_vec = Pica::Texture::LookupTexture(fill_buffer.data(), 0, 0, tex_info) / 255.0f;
const std::array color{color_vec.x, color_vec.y, color_vec.z, color_vec.w};
auto& texture = surface->texture;
texture.Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
cmdbuffer.clearColorImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
color, image_range);
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
return true;
}
default:
LOG_ERROR(Render_Vulkan, "non-color fills not implemented");
return false;
}
}
static vk::Rect2D FromRect(Common::Rectangle<u32> rect) {
vk::Offset2D offset{static_cast<s32>(rect.left), static_cast<s32>(rect.bottom)};
vk::Extent2D extent{rect.GetWidth(), rect.GetHeight()};
@ -412,7 +461,16 @@ void RasterizerCacheVulkan::CopySurface(const Surface& src_surface, const Surfac
// This is only called when CanCopy is true, no need to run checks here
if (src_surface->type == SurfaceType::Fill) {
// NO-OP Vulkan does not allow easy clearing for arbitary textures with rectangle
// FillSurface needs a 4 bytes buffer
const u32 fill_offset =
(boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size;
std::array<u8, 4> fill_buffer;
u32 fill_buff_pos = fill_offset;
for (int i : {0, 1, 2, 3})
fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size];
FillSurface(dst_surface, fill_buffer, dst_surface->GetScaledSubRect(subrect_params));
return;
}
if (src_surface->CanSubRect(subrect_params)) {

View File

@ -33,37 +33,30 @@ using VSOutputAttributes = RasterizerRegs::VSOutputAttributes;
namespace Vulkan {
static const char present_vertex_shader_source[] = R"(
#version 450
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 vert_position;
layout (location = 1) in vec2 vert_tex_coord;
layout (location = 0) out vec2 frag_tex_coord;
layout (location = 1) in vec3 vert_tex_coord;
layout (location = 0) out vec3 frag_tex_coord;
// This is a truncated 3x3 matrix for 2D transformations:
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
// The third column performs translation.
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
// implicitly be [0, 0, 1]
layout (push_constant) uniform DrawInfo {
mat3x2 modelview_matrix;
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int layer;
};
void main() {
// Multiply input position by the rotscale part of the matrix and then manually translate by
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
// to `vec3(vert_position.xy, 1.0)`
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix;
gl_Position = vec4(position.x, -position.y, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}
)";
static const char present_fragment_shader_source[] = R"(
#version 450
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) in vec3 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (push_constant) uniform DrawInfo {
@ -73,10 +66,11 @@ layout (push_constant) uniform DrawInfo {
int layer;
};
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (set = 0, binding = 0) uniform sampler2D screen_textures[3];
void main() {
color = texture(color_texture, frag_tex_coord);
color = texture(screen_textures[int(frag_tex_coord.z)], frag_tex_coord.xy);
//color = vec4(0.5, 0.0, 0.5, 1.0);
}
)";

View File

@ -84,25 +84,26 @@ struct HardwareVertex : public VertexBase {
struct ScreenRectVertexBase {
ScreenRectVertexBase() = default;
ScreenRectVertexBase(float x, float y, float u, float v) {
ScreenRectVertexBase(float x, float y, float u, float v, float s) {
position.x = x;
position.y = y;
tex_coord.x = u;
tex_coord.y = v;
tex_coord.z = s;
}
glm::vec2 position;
glm::vec2 tex_coord;
glm::vec3 tex_coord;
};
struct ScreenRectVertex : public ScreenRectVertexBase {
ScreenRectVertex() = default;
ScreenRectVertex(float x, float y, float u, float v) : ScreenRectVertexBase(x, y, u, v) {};
ScreenRectVertex(float x, float y, float u, float v, float s) : ScreenRectVertexBase(x, y, u, v, s) {};
static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(ScreenRectVertexBase));
static constexpr std::array<vk::VertexInputAttributeDescription, 2> attribute_desc =
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(ScreenRectVertexBase, position)),
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32Sfloat, offsetof(ScreenRectVertexBase, tex_coord)),
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(ScreenRectVertexBase, tex_coord)),
};
};

View File

@ -25,43 +25,56 @@ auto IsStencil = [](vk::Format format) -> bool {
};
};
void DescriptorUpdater::Reset() {
write_count = 0;
buffer_count = 0;
image_count = 0;
}
void DescriptorUpdater::Update() {
assert(update_count > 0);
assert(write_count > 0);
auto device = g_vk_instace->GetDevice();
device.updateDescriptorSets(update_count, writes.data(), 0, nullptr);
device.updateDescriptorSets(write_count, writes.data(), 0, nullptr);
Reset();
}
void DescriptorUpdater::PushTextureArrayUpdate(vk::DescriptorSet set, u32 binding, vk::Sampler sampler,
std::span<vk::ImageView> views) {
ASSERT(image_count < MAX_UPDATES);
u32 start = image_count;
for (auto& view : views) {
image_infos[image_count++] = {sampler, view, vk::ImageLayout::eShaderReadOnlyOptimal};
}
writes[write_count++] = vk::WriteDescriptorSet{set, binding, 0, static_cast<u32>(views.size()),
vk::DescriptorType::eCombinedImageSampler,
image_infos.data() + start};
}
void DescriptorUpdater::PushCombinedImageSamplerUpdate(vk::DescriptorSet set, u32 binding,
vk::Sampler sampler, vk::ImageView view) {
assert(update_count < MAX_DESCRIPTORS);
ASSERT(image_count < MAX_UPDATES);
auto& info = update_queue[update_count];
info.image_info = vk::DescriptorImageInfo{sampler, view, vk::ImageLayout::eShaderReadOnlyOptimal};
image_infos[image_count] = {sampler, view, vk::ImageLayout::eShaderReadOnlyOptimal};
writes[update_count++] = vk::WriteDescriptorSet{
set, binding, 0, 1,
vk::DescriptorType::eCombinedImageSampler,
&info.image_info
};
writes[write_count++] = vk::WriteDescriptorSet{set, binding, 0, 1,
vk::DescriptorType::eCombinedImageSampler,
&image_infos[image_count++]};
}
void DescriptorUpdater::PushBufferUpdate(vk::DescriptorSet set, u32 binding,
vk::DescriptorType buffer_type, u32 offset, u32 size,
vk::Buffer buffer, const vk::BufferView& view) {
assert(update_count < MAX_DESCRIPTORS);
ASSERT(buffer_count < MAX_UPDATES);
auto& info = update_queue[update_count];
info.buffer_info = vk::DescriptorBufferInfo{buffer, offset, size};
info.buffer_view = view;
buffer_infos[buffer_count] = vk::DescriptorBufferInfo{buffer, offset, size};
writes[update_count++] = vk::WriteDescriptorSet{
set, binding, 0, 1,
buffer_type, nullptr,
&info.buffer_info, &info.buffer_view
};
writes[write_count++] = vk::WriteDescriptorSet{set, binding, 0, 1, buffer_type, nullptr,
&buffer_infos[buffer_count++],
view ? &view : nullptr};
}
VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapchain(swapchain) {
@ -169,15 +182,19 @@ void VulkanState::SetTexelBuffer(u32 binding, u32 offset, u32 size, const VKBuff
descriptors_dirty = true;
}
void VulkanState::SetPresentTexture(const VKTexture& image) {
void VulkanState::SetPresentTextures(vk::ImageView view0, vk::ImageView view1, vk::ImageView view2) {
auto& set = descriptor_sets[3];
updater.PushCombinedImageSamplerUpdate(set, 0, present_sampler, image.GetView());
present_view = image.GetView();
std::array views{view0, view1, view2};
updater.PushTextureArrayUpdate(set, 0, present_sampler, views);
descriptors_dirty = true;
}
void VulkanState::SetPresentData(DrawInfo data) {
present_data = data;
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
cmdbuffer.pushConstants(present_pipeline_layout, vk::ShaderStageFlagBits::eFragment |
vk::ShaderStageFlagBits::eVertex, 0, sizeof(data), &data);
}
void VulkanState::SetPlaceholderColor(u8 red, u8 green, u8 blue, u8 alpha) {
@ -480,8 +497,6 @@ void VulkanState::ApplyPresentState() {
// Bind present pipeline and descriptors
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
cmdbuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, present_pipeline.get());
cmdbuffer.pushConstants(present_pipeline_layout, vk::ShaderStageFlagBits::eFragment |
vk::ShaderStageFlagBits::eVertex, 0, sizeof(present_data), &present_data);
ApplyCommonState(false);
@ -561,7 +576,7 @@ void VulkanState::BuildDescriptorLayouts() {
{2, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment} // texture_buffer_lut_rgba
}};
std::array<vk::DescriptorSetLayoutBinding, 1> present_set{{
{0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}
{0, vk::DescriptorType::eCombinedImageSampler, 3, vk::ShaderStageFlagBits::eFragment}
}};
std::array<vk::DescriptorSetLayoutCreateInfo, DESCRIPTOR_SET_COUNT> create_infos{{

View File

@ -21,7 +21,7 @@ template <typename T>
using OptRef = std::optional<std::reference_wrapper<T>>;
struct DrawInfo {
glm::mat2x3 modelview;
glm::mat4 modelview;
glm::vec4 i_resolution;
glm::vec4 o_resolution;
int layer;
@ -32,9 +32,11 @@ public:
DescriptorUpdater() { Reset(); }
~DescriptorUpdater() = default;
void Reset() { update_count = 0; }
void Reset();
void Update();
void PushTextureArrayUpdate(vk::DescriptorSet, u32 biding, vk::Sampler sampler,
std::span<vk::ImageView> views);
void PushCombinedImageSamplerUpdate(vk::DescriptorSet set, u32 binding,
vk::Sampler sampler, vk::ImageView view);
void PushBufferUpdate(vk::DescriptorSet set, u32 binding,
@ -43,15 +45,16 @@ public:
private:
static constexpr u32 MAX_DESCRIPTORS = 10;
static constexpr u32 MAX_UPDATES = 20;
struct Descriptor {
vk::DescriptorImageInfo image_info;
vk::DescriptorBufferInfo buffer_info;
vk::BufferView buffer_view;
};
std::array<vk::WriteDescriptorSet, MAX_DESCRIPTORS> writes;
std::array<Descriptor, MAX_DESCRIPTORS> update_queue;
u32 update_count{};
std::array<vk::DescriptorImageInfo, MAX_UPDATES> image_infos;
std::array<vk::DescriptorBufferInfo, MAX_UPDATES> buffer_infos;
u32 image_count{0}, buffer_count{0}, write_count{0};
};
class VKSwapChain;
@ -105,7 +108,7 @@ public:
void SetUniformBuffer(u32 binding, u32 offset, u32 size, const VKBuffer& buffer);
void SetTexture(u32 binding, const VKTexture& texture);
void SetTexelBuffer(u32 binding, u32 offset, u32 size, const VKBuffer& buffer, u32 view_index);
void SetPresentTexture(const VKTexture& image);
void SetPresentTextures(vk::ImageView view0, vk::ImageView view1, vk::ImageView view2);
void SetPresentData(DrawInfo data);
void SetPlaceholderColor(u8 red, u8 green, u8 blue, u8 alpha);
void UnbindTexture(const VKTexture& image);
@ -128,7 +131,6 @@ private:
bool rendering{false};
vk::ImageView present_view;
std::array<vk::ImageView, 4> render_views;
DrawInfo present_data;
vk::Sampler render_sampler, present_sampler;
VKTexture placeholder;

View File

@ -75,6 +75,8 @@ void VKTexture::Create(const Info& create_info) {
info.format = vk::Format::eD32SfloatS8Uint;
}
std::cout << "New surface!\n";
// Create the texture
image_size = info.width * info.height * BytesPerPixel(info.format);
aspect = GetImageAspect(info.format);
@ -136,9 +138,12 @@ void VKTexture::Destroy() {
auto deleter = [this]() {
auto device = g_vk_instace->GetDevice();
device.destroyImage(texture);
device.destroyImageView(view);
device.freeMemory(memory);
if (texture) {
std::cout << "Surface destroyed!\n";
device.destroyImage(texture);
device.destroyImageView(view);
device.freeMemory(memory);
}
};
// Schedule deletion of the texture after it's no longer used
@ -270,7 +275,7 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region,
}
else if (is_d24s8) {
auto data = D24S8ToD32S8(pixels);
std::memcpy(buffer, data.data(), data.size());
std::memcpy(buffer, data.data(), data.size() * sizeof(data[0]));
}
else {
std::memcpy(buffer, pixels.data(), pixels.size());
@ -322,7 +327,7 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
}
else if (is_d24s8) {
auto data = D32S8ToD24S8(memory);
std::memcpy(buffer, data.data(), data.size());
std::memcpy(buffer, data.data(), data.size() * sizeof(data[0]));
}
else {
std::memcpy(buffer, memory.data(), memory.size());