renderer_vulkan: Fix texture allocation and destruction logic

This commit is contained in:
emufan
2022-06-18 15:04:15 +03:00
parent 4dd4f2170d
commit 816a846cfb
17 changed files with 208 additions and 153 deletions

View File

@ -56,10 +56,6 @@ protected:
constexpr NonCopyable() = default; constexpr NonCopyable() = default;
~NonCopyable() = default; ~NonCopyable() = default;
// Enable std::move operations
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(NonCopyable&&) = default;
NonCopyable(const NonCopyable&) = delete; NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete;
}; };

View File

@ -131,35 +131,6 @@ std::vector<const char*> RequiredExtensions(Frontend::WindowSystemType window_ty
return extensions; return extensions;
} }
/**
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
* corner and (width, height) on the lower-bottom.
*
* The projection part of the matrix is trivial, hence these operations are represented
* by a 3x2 matrix.
*
* @param flipped Whether the frame should be flipped upside down.
*/
static glm::mat3x2 MakeOrthographicMatrix(const float width, const float height, bool flipped) {
glm::mat3x2 matrix; // Laid out in column-major order
// Last matrix row is implicitly assumed to be [0, 0, 1].
if (flipped) {
// clang-format off
matrix[0][0] = 2.f / width; matrix[1][0] = 0.f; matrix[2][0] = -1.f;
matrix[0][1] = 0.f; matrix[1][1] = 2.f / height; matrix[2][1] = -1.f;
// clang-format on
}
else {
// clang-format off
matrix[0][0] = 2.f / width; matrix[1][0] = 0.f; matrix[2][0] = -1.f;
matrix[1][1] = 0.f; matrix[1][1] = -2.f / height; matrix[2][1] = 1.f;
// clang-format on
}
return matrix;
}
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window) RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
: RendererBase{window} { : RendererBase{window} {
@ -304,13 +275,13 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::
switch (screen.format) { switch (screen.format) {
case GPU::Regs::PixelFormat::RGBA8: case GPU::Regs::PixelFormat::RGBA8:
texture_info.format = vk::Format::eR8G8B8A8Srgb; texture_info.format = vk::Format::eR8G8B8A8Unorm;
break; break;
case GPU::Regs::PixelFormat::RGB8: case GPU::Regs::PixelFormat::RGB8:
// Note that the texture will actually give us an RGBA8 image because next to no modern hardware supports RGB formats. // Note that the texture will actually give us an RGBA8 image because next to no modern hardware supports RGB formats.
// The pixels will be converted automatically by Upload() // The pixels will be converted automatically by Upload()
texture_info.format = vk::Format::eR8G8B8Srgb; texture_info.format = vk::Format::eR8G8B8Unorm;
break; break;
case GPU::Regs::PixelFormat::RGB565: case GPU::Regs::PixelFormat::RGB565:
@ -521,7 +492,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
// Set projection matrix // Set projection matrix
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width), draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
static_cast<float>(layout.height), 0.0f, static_cast<float>(layout.height), 0.0f,
-1.f, 1.f)); 0.f, 1.f));
const bool stereo_single_screen = false const bool stereo_single_screen = false
/* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph || /* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
@ -535,7 +506,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
auto& image = swapchain->GetCurrentImage(); auto& image = swapchain->GetCurrentImage();
auto& state = VulkanState::Get(); auto& state = VulkanState::Get();
state.BeginRendering(image, std::nullopt, false, clear_color, vk::AttachmentLoadOp::eClear); state.BeginRendering(&image, nullptr, false, clear_color, vk::AttachmentLoadOp::eClear);
state.SetPresentTextures(screen_infos[0].display_texture->GetView(), state.SetPresentTextures(screen_infos[0].display_texture->GetView(),
screen_infos[1].display_texture->GetView(), screen_infos[1].display_texture->GetView(),
screen_infos[2].display_texture->GetView()); screen_infos[2].display_texture->GetView());
@ -727,7 +698,7 @@ VideoCore::ResultStatus RendererVulkan::Init() {
auto surface = CreateSurface(instance, render_window); auto surface = CreateSurface(instance, render_window);
g_vk_instace = std::make_unique<VKInstance>(); g_vk_instace = std::make_unique<VKInstance>();
g_vk_task_scheduler = std::make_unique<VKTaskScheduler>(); g_vk_task_scheduler = std::make_unique<VKTaskScheduler>();
g_vk_instace->Create(instance, physical_devices[0], surface, true); g_vk_instace->Create(instance, physical_devices[1], surface, true);
g_vk_task_scheduler->Create(); g_vk_task_scheduler->Create();
//auto& layout = render_window.GetFramebufferLayout(); //auto& layout = render_window.GetFramebufferLayout();

View File

@ -28,12 +28,12 @@ void VKBuffer::Create(const VKBuffer::Info& info) {
auto memory_type_index = FindMemoryType(mem_requirements.memoryTypeBits, info.properties); auto memory_type_index = FindMemoryType(mem_requirements.memoryTypeBits, info.properties);
vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index); vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index);
buffer_memory = device.allocateMemory(alloc_info); memory = device.allocateMemory(alloc_info);
device.bindBufferMemory(buffer, buffer_memory, 0); device.bindBufferMemory(buffer, memory, 0);
// Optionally map the buffer to CPU memory // Optionally map the buffer to CPU memory
if (info.properties & vk::MemoryPropertyFlagBits::eHostVisible) { if (info.properties & vk::MemoryPropertyFlagBits::eHostVisible) {
host_ptr = device.mapMemory(buffer_memory, 0, info.size); host_ptr = device.mapMemory(memory, 0, info.size);
} }
for (auto& format : info.view_formats) { for (auto& format : info.view_formats) {
@ -51,13 +51,16 @@ void VKBuffer::Recreate() {
void VKBuffer::Destroy() { void VKBuffer::Destroy() {
if (buffer) { if (buffer) {
if (host_ptr != nullptr) { if (host_ptr != nullptr) {
g_vk_instace->GetDevice().unmapMemory(buffer_memory); g_vk_instace->GetDevice().unmapMemory(memory);
} }
auto deleter = [this]() { auto deleter = [buffer = buffer,
memory = memory,
view_count = view_count,
views = views]() {
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
device.destroyBuffer(buffer); device.destroyBuffer(buffer);
device.freeMemory(buffer_memory); device.freeMemory(memory);
for (u32 i = 0; i < view_count; i++) { for (u32 i = 0; i < view_count; i++) {
device.destroyBufferView(views[i]); device.destroyBufferView(views[i]);

View File

@ -54,7 +54,7 @@ public:
protected: protected:
Info buffer_info; Info buffer_info;
vk::Buffer buffer; vk::Buffer buffer;
vk::DeviceMemory buffer_memory; vk::DeviceMemory memory;
void* host_ptr = nullptr; void* host_ptr = nullptr;
std::array<vk::BufferView, MAX_BUFFER_VIEWS> views; std::array<vk::BufferView, MAX_BUFFER_VIEWS> views;
u32 view_count{}; u32 view_count{};

View File

@ -7,22 +7,21 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#if defined(VK_EXT_color_write_enable)
PFN_vkCmdSetColorWriteEnableEXT ptr_vkCmdSetColorWriteEnableEXT;
#endif /* defined(VK_EXT_color_write_enable) */
namespace Vulkan { namespace Vulkan {
std::unique_ptr<VKInstance> g_vk_instace; std::unique_ptr<VKInstance> g_vk_instace;
VKInstance::~VKInstance() { VKInstance::~VKInstance() {
device.waitIdle();
device.destroy();
instance.destroy();
} }
bool VKInstance::Create(vk::Instance instance, vk::PhysicalDevice physical_device, bool VKInstance::Create(vk::Instance new_instance, vk::PhysicalDevice gpu,
vk::SurfaceKHR surface, bool enable_validation_layer) { vk::SurfaceKHR surface, bool enable_validation_layer) {
this->instance = instance; instance = new_instance;
this->physical_device = physical_device; physical_device = gpu;
// Get physical device limits // Get physical device limits
device_limits = physical_device.getProperties().limits; device_limits = physical_device.getProperties().limits;
@ -98,15 +97,11 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) {
} }
// Create logical device // Create logical device
device = physical_device.createDeviceUnique(device_info); device = physical_device.createDevice(device_info);
#if defined(VK_EXT_color_write_enable)
ptr_vkCmdSetColorWriteEnableEXT = reinterpret_cast<PFN_vkCmdSetColorWriteEnableEXT>(device->getProcAddr("vkCmdSetColorWriteEnableEXT"));
#endif /* defined(VK_EXT_color_write_enable) */
// Grab the graphics and present queues. // Grab the graphics and present queues.
graphics_queue = device->getQueue(graphics_queue_family_index, 0); graphics_queue = device.getQueue(graphics_queue_family_index, 0);
present_queue = device->getQueue(present_queue_family_index, 0); present_queue = device.getQueue(present_queue_family_index, 0);
return true; return true;
} }
@ -195,7 +190,3 @@ bool VKInstance::FindExtensions() {
} }
} // namespace Vulkan } // namespace Vulkan
void vkCmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkBool32* pColorWriteEnables) {
ptr_vkCmdSetColorWriteEnableEXT(commandBuffer, attachmentCount, pColorWriteEnables);
}

View File

@ -22,8 +22,9 @@ public:
bool Create(vk::Instance instance, vk::PhysicalDevice gpu, bool Create(vk::Instance instance, vk::PhysicalDevice gpu,
vk::SurfaceKHR surface, bool enable_validation_layer); vk::SurfaceKHR surface, bool enable_validation_layer);
vk::Device GetDevice() const { return device.get(); } vk::Device GetDevice() const { return device; }
vk::PhysicalDevice GetPhysicalDevice() const { return physical_device; } vk::PhysicalDevice GetPhysicalDevice() const { return physical_device; }
vk::Instance GetInstance() const { return instance; }
/// Retrieve queue information /// Retrieve queue information
u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; } u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; }
@ -46,9 +47,9 @@ public:
vk::Queue present_queue, graphics_queue; vk::Queue present_queue, graphics_queue;
// Core vulkan objects // Core vulkan objects
vk::Instance instance;
vk::PhysicalDevice physical_device; vk::PhysicalDevice physical_device;
vk::UniqueDevice device; vk::Instance instance;
vk::Device device;
// Extensions and features // Extensions and features
std::vector<const char*> extensions; std::vector<const char*> extensions;
@ -67,7 +68,3 @@ public:
extern std::unique_ptr<VKInstance> g_vk_instace; extern std::unique_ptr<VKInstance> g_vk_instace;
} // namespace Vulkan } // namespace Vulkan
#if defined(VK_EXT_color_write_enable)
extern PFN_vkCmdSetColorWriteEnableEXT ptr_vkCmdSetColorWriteEnableEXT;
#endif /* defined(VK_EXT_color_write_enable) */

View File

@ -367,7 +367,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
state.SetScissor(scissor); state.SetScissor(scissor);
// Bind the framebuffer surfaces // Bind the framebuffer surfaces
state.BeginRendering(color_surface->texture, depth_surface->texture, true); state.BeginRendering(color_surface != nullptr ? &color_surface->texture : nullptr,
depth_surface != nullptr ? &depth_surface->texture : nullptr, true);
state.ApplyRenderState(Pica::g_state.regs); state.ApplyRenderState(Pica::g_state.regs);
state.SetVertexBuffer(vertex_buffer, 0); state.SetVertexBuffer(vertex_buffer, 0);
@ -399,8 +400,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
} }
state.EndRendering(); state.EndRendering();
color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); if (color_surface) {
color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
}
if (depth_surface) {
depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
}
g_vk_task_scheduler->Submit(); g_vk_task_scheduler->Submit();

View File

@ -42,8 +42,8 @@ using SurfaceType = SurfaceParams::SurfaceType;
using PixelFormat = SurfaceParams::PixelFormat; using PixelFormat = SurfaceParams::PixelFormat;
static constexpr std::array<vk::Format, 5> fb_format_tuples = {{ static constexpr std::array<vk::Format, 5> fb_format_tuples = {{
vk::Format::eR8G8B8A8Srgb, // RGBA8 vk::Format::eR8G8B8A8Unorm, // RGBA8
vk::Format::eR8G8B8Srgb, // RGB8 vk::Format::eR8G8B8Unorm, // RGB8
vk::Format::eR5G5B5A1UnormPack16, // RGB5A1 vk::Format::eR5G5B5A1UnormPack16, // RGB5A1
vk::Format::eR5G6B5UnormPack16, // RGB565 vk::Format::eR5G6B5UnormPack16, // RGB565
vk::Format::eR4G4B4A4UnormPack16, // RGBA4 vk::Format::eR4G4B4A4UnormPack16, // RGBA4
@ -66,7 +66,7 @@ vk::Format GetFormatTuple(PixelFormat pixel_format) {
ASSERT(tuple_idx < depth_format_tuples.size()); ASSERT(tuple_idx < depth_format_tuples.size());
return depth_format_tuples[tuple_idx]; return depth_format_tuples[tuple_idx];
} }
return vk::Format::eR8G8B8A8Srgb; return vk::Format::eR8G8B8A8Unorm;
} }
template <typename Map, typename Interval> template <typename Map, typename Interval>
@ -293,6 +293,7 @@ static bool FillSurface(const Surface& surface, std::array<u8, 4> fill_buffer,
if (surface->GetScaledRect() != rect) { if (surface->GetScaledRect() != rect) {
// TODO: use vkCmdClearAttachments to clear subrects // TODO: use vkCmdClearAttachments to clear subrects
LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented"); LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented");
return false;
} }
vk::ImageSubresourceRange image_range{{}, 0, 1, 0, 1}; vk::ImageSubresourceRange image_range{{}, 0, 1, 0, 1};
@ -327,12 +328,36 @@ static bool FillSurface(const Surface& surface, std::array<u8, 4> fill_buffer,
cmdbuffer.clearColorImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal, cmdbuffer.clearColorImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
color, image_range); color, image_range);
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
return true;
}
case SurfaceParams::SurfaceType::Depth:
case SurfaceParams::SurfaceType::DepthStencil: {
auto& texture = surface->texture;
texture.Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
u32 value_32bit = 0;
vk::ClearDepthStencilValue clear_value;
if (surface->pixel_format == SurfaceParams::PixelFormat::D16) {
std::memcpy(&value_32bit, fill_buffer.data(), sizeof(u16));
clear_value.depth = value_32bit / 65535.0f; // 2^16 - 1
} else if (surface->pixel_format == SurfaceParams::PixelFormat::D24) {
std::memcpy(&value_32bit, fill_buffer.data(), 3);
clear_value.depth = value_32bit / 16777215.0f; // 2^24 - 1
} else {
std::memcpy(&value_32bit, fill_buffer.data(), sizeof(u32));
clear_value.depth = (value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
clear_value.stencil = value_32bit >> 24;
}
cmdbuffer.clearDepthStencilImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
clear_value, image_range);
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
return true; return true;
} }
default: default:
LOG_ERROR(Render_Vulkan, "non-color fills not implemented"); LOG_CRITICAL(Render_Vulkan, "Unsupported fill operation requested!");
return false; return false;
} }
} }
@ -344,14 +369,14 @@ static vk::Rect2D FromRect(Common::Rectangle<u32> rect) {
} }
// Allocate an uninitialized texture of appropriate size and format for the surface // Allocate an uninitialized texture of appropriate size and format for the surface
VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Format format, void RasterizerCacheVulkan::AllocateTexture(VKTexture& target, SurfaceType type, vk::Format format,
u32 width, u32 height) { u32 width, u32 height) {
// First check if the texture can be recycled // First check if the texture can be recycled
auto recycled_tex = host_texture_recycler.find({format, width, height}); auto recycled_tex = host_texture_recycler.find({format, width, height});
if (recycled_tex != host_texture_recycler.end()) { if (recycled_tex != host_texture_recycler.end()) {
VKTexture texture = std::move(recycled_tex->second); target = std::move(recycled_tex->second);
host_texture_recycler.erase(recycled_tex); host_texture_recycler.erase(recycled_tex);
return texture; return;
} }
auto GetUsage = [](SurfaceType type) { auto GetUsage = [](SurfaceType type) {
@ -378,7 +403,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
// Otherwise create a brand new texture // Otherwise create a brand new texture
u32 levels = std::log2(std::max(width, height)) + 1; u32 levels = std::log2(std::max(width, height)) + 1;
VKTexture::Info texture_info = { VKTexture::Info texture_info{
.width = width, .width = width,
.height = height, .height = height,
.format = format, .format = format,
@ -388,13 +413,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
.levels = levels .levels = levels
}; };
VKTexture texture;
texture.Create(texture_info);
auto cmdbuffer = g_vk_task_scheduler->GetUploadCommandBuffer(); auto cmdbuffer = g_vk_task_scheduler->GetUploadCommandBuffer();
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
return texture; target.Destroy();
target.Create(texture_info);
target.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
} }
CachedSurface::~CachedSurface() { CachedSurface::~CachedSurface() {
@ -1431,8 +1454,8 @@ Surface RasterizerCacheVulkan::CreateSurface(const SurfaceParams& params) {
static_cast<SurfaceParams&>(*surface) = params; static_cast<SurfaceParams&>(*surface) = params;
surface->invalid_regions.insert(surface->GetInterval()); surface->invalid_regions.insert(surface->GetInterval());
surface->texture = AllocateSurfaceTexture(params.type, GetFormatTuple(surface->pixel_format), AllocateTexture(surface->texture, params.type, GetFormatTuple(surface->pixel_format),
surface->GetScaledWidth(), surface->GetScaledHeight()); surface->GetScaledWidth(), surface->GetScaledHeight());
return surface; return surface;
} }

View File

@ -342,7 +342,7 @@ private:
std::recursive_mutex mutex; std::recursive_mutex mutex;
public: public:
VKTexture AllocateSurfaceTexture(SurfaceParams::SurfaceType type, vk::Format format, void AllocateTexture(VKTexture& target, SurfaceParams::SurfaceType type, vk::Format format,
u32 width, u32 height); u32 width, u32 height);
std::unique_ptr<FormatReinterpreterVulkan> format_reinterpreter; std::unique_ptr<FormatReinterpreterVulkan> format_reinterpreter;
}; };

View File

@ -1539,6 +1539,7 @@ vec4 secondary_fragment_color = vec4(0.0);
// do our own transformation according to PICA specification. // do our own transformation according to PICA specification.
out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n" out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n"
"float depth = z_over_w * depth_scale + depth_offset;\n"; "float depth = z_over_w * depth_scale + depth_offset;\n";
//out += "if (gl_FragCoord.z == 0) { color = vec4(1.0, 0.0, 0.0, 1.0); return; }\n";
if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) { if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) {
out += "depth /= gl_FragCoord.w;\n"; out += "depth /= gl_FragCoord.w;\n";
} }
@ -1655,11 +1656,14 @@ void main() {
texcoord0_w = vert_texcoord0_w; texcoord0_w = vert_texcoord0_w;
normquat = vert_normquat; normquat = vert_normquat;
view = vert_view; view = vert_view;
gl_Position = vert_position + vec4(0.0, 0.0, 1.0, 0.0);
#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) // Convert OpenGL [-1, 1] to Vulkan [0, 1]
//gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 vec4 position = vert_position;
//gl_ClipDistance[1] = dot(clip_coef, vert_position); position.z = (position.z + 1) * 0.5;
#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
gl_Position = position;
gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
gl_ClipDistance[1] = dot(clip_coef, vert_position);
} }
)"; )";

View File

@ -7,6 +7,7 @@
#include "video_core/renderer_vulkan/vk_state.h" #include "video_core/renderer_vulkan/vk_state.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_task_scheduler.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h"
#include "video_core/renderer_vulkan/vk_rasterizer_cache.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_shader_gen.h" #include "video_core/renderer_vulkan/vk_shader_gen.h"
@ -82,7 +83,7 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
VKTexture::Info info{ VKTexture::Info info{
.width = 1, .width = 1,
.height = 1, .height = 1,
.format = vk::Format::eR8G8B8A8Srgb, .format = vk::Format::eR8G8B8A8Unorm,
.type = vk::ImageType::e2D, .type = vk::ImageType::e2D,
.view_type = vk::ImageViewType::e2D, .view_type = vk::ImageViewType::e2D,
.usage = vk::ImageUsageFlagBits::eSampled | .usage = vk::ImageUsageFlagBits::eSampled |
@ -124,9 +125,12 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
VulkanState::~VulkanState() { VulkanState::~VulkanState() {
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
device.waitIdle();
// Destroy vertex shader // Destroy vertex shader
device.destroyShaderModule(render_vertex_shader); device.destroyShaderModule(render_vertex_shader);
device.destroyShaderModule(present_vertex_shader);
device.destroyShaderModule(present_fragment_shader);
// Destroy pipeline layouts // Destroy pipeline layouts
device.destroyPipelineLayout(render_pipeline_layout); device.destroyPipelineLayout(render_pipeline_layout);
@ -227,7 +231,7 @@ void VulkanState::UnbindTexture(u32 unit) {
descriptors_dirty = true; descriptors_dirty = true;
} }
void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> depth, bool update_pipeline_formats, void VulkanState::BeginRendering(VKTexture* color, VKTexture* depth, bool update_pipeline_formats,
vk::ClearColorValue color_clear, vk::AttachmentLoadOp color_load_op, vk::ClearColorValue color_clear, vk::AttachmentLoadOp color_load_op,
vk::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear, vk::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear,
vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op, vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op,
@ -236,38 +240,37 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
EndRendering(); EndRendering();
// Make sure attachments are in optimal layout // Make sure attachments are in optimal layout
vk::RenderingInfo render_info{{}, color->get().GetArea(), 1, {}}; vk::RenderingInfo render_info{{}, {}, 1, {}};
std::array<vk::RenderingAttachmentInfo, 3> infos{}; std::array<vk::RenderingAttachmentInfo, 3> infos{};
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer(); auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
if (color.has_value()) { if (color != nullptr) {
auto& image = color->get(); color->Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
image.Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
infos[0] = vk::RenderingAttachmentInfo{ infos[0] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {}, color->GetView(), color->GetLayout(), {}, {}, {},
color_load_op, color_store_op, color_clear color_load_op, color_store_op, color_clear
}; };
render_info.colorAttachmentCount = 1; render_info.colorAttachmentCount = 1;
render_info.pColorAttachments = &infos[0]; render_info.pColorAttachments = &infos[0];
render_info.renderArea = color->GetArea();
} }
if (depth.has_value()) { if (depth != nullptr) {
auto& image = depth->get(); depth->Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
image.Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
infos[1] = vk::RenderingAttachmentInfo{ infos[1] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {}, depth->GetView(), depth->GetLayout(), {}, {}, {},
depth_load_op, depth_store_op, depth_clear depth_load_op, depth_store_op, depth_clear
}; };
render_info.pDepthAttachment = &infos[1]; render_info.pDepthAttachment = &infos[1];
if (IsStencil(image.GetFormat())) { if (IsStencil(depth->GetFormat())) {
infos[2] = vk::RenderingAttachmentInfo{ infos[2] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {}, depth->GetView(), depth->GetLayout(), {}, {}, {},
stencil_load_op, stencil_store_op, depth_clear stencil_load_op, stencil_store_op, depth_clear
}; };
@ -276,8 +279,12 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
} }
if (update_pipeline_formats) { if (update_pipeline_formats) {
render_pipeline_key.color = color.has_value() ? color->get().GetFormat() : vk::Format::eUndefined; render_pipeline_key.color = color != nullptr ?
render_pipeline_key.depth_stencil = depth.has_value() ? depth->get().GetFormat() : vk::Format::eUndefined; color->GetFormat() :
vk::Format::eUndefined;
render_pipeline_key.depth_stencil = depth != nullptr ?
depth->GetFormat() :
vk::Format::eUndefined;
} }
// Begin rendering // Begin rendering

View File

@ -17,9 +17,6 @@ namespace Vulkan {
constexpr u32 DESCRIPTOR_SET_COUNT = 4; constexpr u32 DESCRIPTOR_SET_COUNT = 4;
template <typename T>
using OptRef = std::optional<std::reference_wrapper<T>>;
struct DrawInfo { struct DrawInfo {
glm::mat4 modelview; glm::mat4 modelview;
glm::vec4 i_resolution; glm::vec4 i_resolution;
@ -93,7 +90,7 @@ public:
vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha); vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha);
/// Rendering /// Rendering
void BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> depth, bool update_pipeline_formats = false, void BeginRendering(VKTexture* color, VKTexture* depth, bool update_pipeline_formats = false,
vk::ClearColorValue color_clear = {}, vk::ClearColorValue color_clear = {},
vk::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad, vk::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad,
vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore, vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore,

View File

@ -19,9 +19,13 @@ VKSwapChain::VKSwapChain(vk::SurfaceKHR surface_) : surface(surface_) {
VKSwapChain::~VKSwapChain() { VKSwapChain::~VKSwapChain() {
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
auto instance = g_vk_instace->GetInstance();
device.waitIdle(); device.waitIdle();
device.destroySemaphore(render_finished);
device.destroySemaphore(image_available);
device.destroySwapchainKHR(swapchain); device.destroySwapchainKHR(swapchain);
instance.destroySurfaceKHR(surface);
} }
bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) { bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) {
@ -59,11 +63,11 @@ bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) {
// Create sync objects if not already created // Create sync objects if not already created
if (!image_available) { if (!image_available) {
image_available = device.createSemaphoreUnique({}); image_available = device.createSemaphore({});
} }
if (!render_finished) { if (!render_finished) {
render_finished = device.createSemaphoreUnique({}); render_finished = device.createSemaphore({});
} }
// Create framebuffer and image views // Create framebuffer and image views
@ -78,7 +82,7 @@ constexpr u64 ACQUIRE_TIMEOUT = 1000000000;
void VKSwapChain::AcquireNextImage() { void VKSwapChain::AcquireNextImage() {
auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT, auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT,
image_available.get(), VK_NULL_HANDLE, image_available, VK_NULL_HANDLE,
&image_index); &image_index);
switch (result) { switch (result) {
case vk::Result::eSuccess: case vk::Result::eSuccess:
@ -98,7 +102,7 @@ void VKSwapChain::AcquireNextImage() {
void VKSwapChain::Present() { void VKSwapChain::Present() {
const auto present_queue = g_vk_instace->GetPresentQueue(); const auto present_queue = g_vk_instace->GetPresentQueue();
vk::PresentInfoKHR present_info(render_finished.get(), swapchain, image_index); vk::PresentInfoKHR present_info(render_finished, swapchain, image_index);
vk::Result result = present_queue.presentKHR(present_info); vk::Result result = present_queue.presentKHR(present_info);
switch (result) { switch (result) {

View File

@ -42,8 +42,8 @@ public:
vk::SurfaceKHR GetSurface() const { return surface; } vk::SurfaceKHR GetSurface() const { return surface; }
vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; } vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; }
vk::SwapchainKHR GetSwapChain() const { return swapchain; } vk::SwapchainKHR GetSwapChain() const { return swapchain; }
const vk::Semaphore& GetAvailableSemaphore() const { return image_available.get(); } const vk::Semaphore& GetAvailableSemaphore() const { return image_available; }
const vk::Semaphore& GetRenderSemaphore() const { return render_finished.get(); } const vk::Semaphore& GetRenderSemaphore() const { return render_finished; }
VKTexture& GetCurrentImage() { return swapchain_images[image_index]; } VKTexture& GetCurrentImage() { return swapchain_images[image_index]; }
private: private:
@ -53,7 +53,7 @@ private:
private: private:
SwapChainDetails details{}; SwapChainDetails details{};
vk::SurfaceKHR surface; vk::SurfaceKHR surface;
vk::UniqueSemaphore image_available, render_finished; vk::Semaphore image_available, render_finished;
bool vsync_enabled{false}, is_outdated{true}, is_suboptimal{true}; bool vsync_enabled{false}, is_outdated{true}, is_suboptimal{true};
vk::SwapchainKHR swapchain{VK_NULL_HANDLE}; vk::SwapchainKHR swapchain{VK_NULL_HANDLE};

View File

@ -11,10 +11,11 @@
namespace Vulkan { namespace Vulkan {
VKTaskScheduler::~VKTaskScheduler() { VKTaskScheduler::~VKTaskScheduler() {
SyncToGPU();
// Destroy Vulkan resources // Destroy Vulkan resources
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
device.waitIdle();
SyncToGPU();
device.destroyCommandPool(command_pool); device.destroyCommandPool(command_pool);
device.destroySemaphore(timeline); device.destroySemaphore(timeline);
@ -63,7 +64,8 @@ bool VKTaskScheduler::Create() {
.size = STAGING_BUFFER_SIZE, .size = STAGING_BUFFER_SIZE,
.properties = vk::MemoryPropertyFlagBits::eHostVisible | .properties = vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent, vk::MemoryPropertyFlagBits::eHostCoherent,
.usage = vk::BufferUsageFlagBits::eTransferSrc .usage = vk::BufferUsageFlagBits::eTransferSrc |
vk::BufferUsageFlagBits::eTransferDst
}; };
// Should be enough for a single frame // Should be enough for a single frame
@ -131,6 +133,7 @@ void VKTaskScheduler::SyncToGPU(u64 task_index) {
for (auto& func : task.cleanups) { for (auto& func : task.cleanups) {
func(); func();
} }
task.cleanups.clear();
} }
} }
} }

View File

@ -19,10 +19,10 @@ static int BytesPerPixel(vk::Format format) {
case vk::Format::eD32Sfloat: case vk::Format::eD32Sfloat:
case vk::Format::eB8G8R8A8Unorm: case vk::Format::eB8G8R8A8Unorm:
case vk::Format::eR8G8B8A8Uint: case vk::Format::eR8G8B8A8Uint:
case vk::Format::eR8G8B8A8Srgb: case vk::Format::eR8G8B8A8Unorm:
case vk::Format::eD24UnormS8Uint: case vk::Format::eD24UnormS8Uint:
return 4; return 4;
case vk::Format::eR8G8B8Uint: case vk::Format::eR8G8B8Unorm:
case vk::Format::eR8G8B8Srgb: case vk::Format::eR8G8B8Srgb:
return 3; return 3;
case vk::Format::eR5G6B5UnormPack16: case vk::Format::eR5G6B5UnormPack16:
@ -58,15 +58,41 @@ VKTexture::~VKTexture() {
Destroy(); Destroy();
} }
VKTexture::VKTexture(VKTexture&& other) noexcept {
info = std::exchange(other.info, Info{});
texture = std::exchange(other.texture, VK_NULL_HANDLE);
aspect = std::exchange(other.aspect, vk::ImageAspectFlagBits::eNone);
view = std::exchange(other.view, VK_NULL_HANDLE);
memory = std::exchange(other.memory, VK_NULL_HANDLE);
image_size = std::exchange(other.image_size, 0);
adopted = std::exchange(other.adopted, false);
is_rgb = std::exchange(other.is_rgb, false);
is_d24s8 = std::exchange(other.is_d24s8, false);
}
VKTexture& VKTexture::operator=(VKTexture&& other) noexcept {
Destroy();
info = std::exchange(other.info, Info{});
texture = std::exchange(other.texture, VK_NULL_HANDLE);
aspect = std::exchange(other.aspect, vk::ImageAspectFlagBits::eNone);
view = std::exchange(other.view, VK_NULL_HANDLE);
memory = std::exchange(other.memory, VK_NULL_HANDLE);
image_size = std::exchange(other.image_size, 0);
adopted = std::exchange(other.adopted, false);
is_rgb = std::exchange(other.is_rgb, false);
is_d24s8 = std::exchange(other.is_d24s8, false);
return *this;
}
void VKTexture::Create(const Info& create_info) { void VKTexture::Create(const Info& create_info) {
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
info = create_info; info = create_info;
// Emulate RGB8 format with RGBA8 // Emulate RGB8 format with RGBA8
is_rgb = false; is_rgb = false;
if (info.format == vk::Format::eR8G8B8Srgb) { if (info.format == vk::Format::eR8G8B8Unorm) {
is_rgb = true; is_rgb = true;
info.format = vk::Format::eR8G8B8A8Srgb; info.format = vk::Format::eR8G8B8A8Unorm;
} }
is_d24s8 = false; is_d24s8 = false;
@ -136,7 +162,9 @@ void VKTexture::Destroy() {
auto& state = VulkanState::Get(); auto& state = VulkanState::Get();
state.UnbindTexture(*this); state.UnbindTexture(*this);
auto deleter = [this]() { auto deleter = [texture = texture,
view = view,
memory = memory]() {
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
if (texture) { if (texture) {
std::cout << "Surface destroyed!\n"; std::cout << "Surface destroyed!\n";
@ -154,7 +182,7 @@ void VKTexture::Destroy() {
// If the image was adopted (probably from the swapchain) then only // If the image was adopted (probably from the swapchain) then only
// destroy the view // destroy the view
if (adopted) { if (adopted) {
g_vk_task_scheduler->Schedule([this](){ g_vk_task_scheduler->Schedule([view = view](){
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
device.destroyImageView(view); device.destroyImageView(view);
}); });
@ -281,19 +309,32 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region,
std::memcpy(buffer, pixels.data(), pixels.size()); std::memcpy(buffer, pixels.data(), pixels.size());
} }
vk::BufferImageCopy copy_region{ std::array<vk::BufferImageCopy, 2> copy_regions;
u32 region_count = 1;
copy_regions[0] = vk::BufferImageCopy{
offset, row_length, region.extent.height, offset, row_length, region.extent.height,
{aspect, level, layer, 1}, {aspect, level, layer, 1},
{region.offset.x, region.offset.y, 0}, {region.offset.x, region.offset.y, 0},
{region.extent.width, region.extent.height, 1} {region.extent.width, region.extent.height, 1}
}; };
if (aspect & vk::ImageAspectFlagBits::eDepth &&
aspect & vk::ImageAspectFlagBits::eStencil) {
// Copying both depth and stencil requires two seperate regions
copy_regions[1] = copy_regions[0];
copy_regions[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
copy_regions[1].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
region_count++;
}
// Transition image to transfer format // Transition image to transfer format
Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal); Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(), cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(),
texture, vk::ImageLayout::eTransferDstOptimal, texture, vk::ImageLayout::eTransferDstOptimal, region_count,
copy_region); copy_regions.data());
// Prepare image for shader reads // Prepare image for shader reads
Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
@ -301,7 +342,7 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region,
void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> memory) { void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> memory) {
u32 request_size = is_rgb ? (memory.size() / 3) * 4 : u32 request_size = is_rgb ? (memory.size() / 3) * 4 :
(is_d24s8 ? (memory.size() / 4) * 5 : memory.size()); (is_d24s8 ? (memory.size() / 4) * 8 : memory.size());
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(request_size); auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(request_size);
if (!buffer) { if (!buffer) {
LOG_ERROR(Render_Vulkan, "Cannot download texture without staging buffer!"); LOG_ERROR(Render_Vulkan, "Cannot download texture without staging buffer!");
@ -312,25 +353,24 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer(); auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
// Copy pixels to staging buffer std::array<vk::BufferImageCopy, 2> copy_regions;
vk::BufferImageCopy download_region{ u32 region_count = 1;
copy_regions[0] = vk::BufferImageCopy{
offset, row_length, region.extent.height, offset, row_length, region.extent.height,
{aspect, level, layer, 1}, {aspect, level, layer, 1},
{region.offset.x, region.offset.y, 0}, {region.offset.x, region.offset.y, 0},
{region.extent.width, region.extent.height, 1} {region.extent.width, region.extent.height, 1}
}; };
// Automatically convert RGB to RGBA if (aspect & vk::ImageAspectFlagBits::eDepth &&
if (is_rgb) { aspect & vk::ImageAspectFlagBits::eStencil) {
auto data = RGBAToRGB(memory); // Copying both depth and stencil requires two seperate regions
std::memcpy(buffer, data.data(), data.size()); copy_regions[1] = copy_regions[0];
} copy_regions[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
else if (is_d24s8) { copy_regions[1].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
auto data = D32S8ToD24S8(memory);
std::memcpy(buffer, data.data(), data.size() * sizeof(data[0])); region_count++;
}
else {
std::memcpy(buffer, memory.data(), memory.size());
} }
// Transition image to transfer format // Transition image to transfer format
@ -338,16 +378,28 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal); Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal);
cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal, cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal,
g_vk_task_scheduler->GetStaging().GetBuffer(), g_vk_task_scheduler->GetStaging().GetBuffer(),
download_region); region_count, copy_regions.data());
// Restore layout
Transition(cmdbuffer, old_layout);
// Wait for the data to be available // Wait for the data to be available
// NOTE: This is really slow and should be reworked // NOTE: This is really slow and should be reworked
g_vk_task_scheduler->Submit(true); g_vk_task_scheduler->Submit(true);
std::memcpy(memory.data(), buffer, memory.size_bytes());
// Restore layout // Automatically convert RGB to RGBA
Transition(cmdbuffer, old_layout); if (is_rgb) {
auto data = RGBAToRGB(std::span(buffer, request_size));
std::memcpy(memory.data(), data.data(), memory.size());
}
else if (is_d24s8) {
auto data = D32S8ToD24S8(std::span(buffer, request_size));
std::memcpy(memory.data(), data.data(), memory.size());
}
else {
std::memcpy(memory.data(), buffer, memory.size());
}
} }
template <typename Out, typename In> template <typename Out, typename In>

View File

@ -32,8 +32,8 @@ public:
~VKTexture(); ~VKTexture();
/// Enable move operations /// Enable move operations
VKTexture(VKTexture&&) = default; VKTexture(VKTexture&& other) noexcept;
VKTexture& operator=(VKTexture&&) = default; VKTexture& operator=(VKTexture&& other) noexcept;
/// Create a new Vulkan texture object /// Create a new Vulkan texture object
void Create(const Info& info); void Create(const Info& info);