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;
~NonCopyable() = default;
// Enable std::move operations
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(NonCopyable&&) = default;
NonCopyable(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;
}
/**
* 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)
: RendererBase{window} {
@ -304,13 +275,13 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::
switch (screen.format) {
case GPU::Regs::PixelFormat::RGBA8:
texture_info.format = vk::Format::eR8G8B8A8Srgb;
texture_info.format = vk::Format::eR8G8B8A8Unorm;
break;
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.
// The pixels will be converted automatically by Upload()
texture_info.format = vk::Format::eR8G8B8Srgb;
texture_info.format = vk::Format::eR8G8B8Unorm;
break;
case GPU::Regs::PixelFormat::RGB565:
@ -521,7 +492,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
// Set projection matrix
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
static_cast<float>(layout.height), 0.0f,
-1.f, 1.f));
0.f, 1.f));
const bool stereo_single_screen = false
/* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
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& 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(),
screen_infos[1].display_texture->GetView(),
screen_infos[2].display_texture->GetView());
@ -727,7 +698,7 @@ VideoCore::ResultStatus RendererVulkan::Init() {
auto surface = CreateSurface(instance, render_window);
g_vk_instace = std::make_unique<VKInstance>();
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();
//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);
vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index);
buffer_memory = device.allocateMemory(alloc_info);
device.bindBufferMemory(buffer, buffer_memory, 0);
memory = device.allocateMemory(alloc_info);
device.bindBufferMemory(buffer, memory, 0);
// Optionally map the buffer to CPU memory
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) {
@ -51,13 +51,16 @@ void VKBuffer::Recreate() {
void VKBuffer::Destroy() {
if (buffer) {
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();
device.destroyBuffer(buffer);
device.freeMemory(buffer_memory);
device.freeMemory(memory);
for (u32 i = 0; i < view_count; i++) {
device.destroyBufferView(views[i]);

View File

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

View File

@ -7,22 +7,21 @@
#include "common/logging/log.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 {
std::unique_ptr<VKInstance> g_vk_instace;
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) {
this->instance = instance;
this->physical_device = physical_device;
instance = new_instance;
physical_device = gpu;
// Get physical device limits
device_limits = physical_device.getProperties().limits;
@ -98,15 +97,11 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) {
}
// Create logical device
device = physical_device.createDeviceUnique(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) */
device = physical_device.createDevice(device_info);
// Grab the graphics and present queues.
graphics_queue = device->getQueue(graphics_queue_family_index, 0);
present_queue = device->getQueue(present_queue_family_index, 0);
graphics_queue = device.getQueue(graphics_queue_family_index, 0);
present_queue = device.getQueue(present_queue_family_index, 0);
return true;
}
@ -195,7 +190,3 @@ bool VKInstance::FindExtensions() {
}
} // 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,
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::Instance GetInstance() const { return instance; }
/// Retrieve queue information
u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; }
@ -46,9 +47,9 @@ public:
vk::Queue present_queue, graphics_queue;
// Core vulkan objects
vk::Instance instance;
vk::PhysicalDevice physical_device;
vk::UniqueDevice device;
vk::Instance instance;
vk::Device device;
// Extensions and features
std::vector<const char*> extensions;
@ -67,7 +68,3 @@ public:
extern std::unique_ptr<VKInstance> g_vk_instace;
} // 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);
// 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.SetVertexBuffer(vertex_buffer, 0);
@ -399,8 +400,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
}
state.EndRendering();
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();

View File

@ -42,8 +42,8 @@ using SurfaceType = SurfaceParams::SurfaceType;
using PixelFormat = SurfaceParams::PixelFormat;
static constexpr std::array<vk::Format, 5> fb_format_tuples = {{
vk::Format::eR8G8B8A8Srgb, // RGBA8
vk::Format::eR8G8B8Srgb, // RGB8
vk::Format::eR8G8B8A8Unorm, // RGBA8
vk::Format::eR8G8B8Unorm, // RGB8
vk::Format::eR5G5B5A1UnormPack16, // RGB5A1
vk::Format::eR5G6B5UnormPack16, // RGB565
vk::Format::eR4G4B4A4UnormPack16, // RGBA4
@ -66,7 +66,7 @@ vk::Format GetFormatTuple(PixelFormat pixel_format) {
ASSERT(tuple_idx < depth_format_tuples.size());
return depth_format_tuples[tuple_idx];
}
return vk::Format::eR8G8B8A8Srgb;
return vk::Format::eR8G8B8A8Unorm;
}
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) {
// TODO: use vkCmdClearAttachments to clear subrects
LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented");
return false;
}
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,
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);
return true;
}
default:
LOG_ERROR(Render_Vulkan, "non-color fills not implemented");
LOG_CRITICAL(Render_Vulkan, "Unsupported fill operation requested!");
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
VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Format format,
void RasterizerCacheVulkan::AllocateTexture(VKTexture& target, SurfaceType type, vk::Format format,
u32 width, u32 height) {
// First check if the texture can be recycled
auto recycled_tex = host_texture_recycler.find({format, width, height});
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);
return texture;
return;
}
auto GetUsage = [](SurfaceType type) {
@ -378,7 +403,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
// Otherwise create a brand new texture
u32 levels = std::log2(std::max(width, height)) + 1;
VKTexture::Info texture_info = {
VKTexture::Info texture_info{
.width = width,
.height = height,
.format = format,
@ -388,13 +413,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
.levels = levels
};
VKTexture texture;
texture.Create(texture_info);
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() {
@ -1431,7 +1454,7 @@ Surface RasterizerCacheVulkan::CreateSurface(const SurfaceParams& params) {
static_cast<SurfaceParams&>(*surface) = params;
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());
return surface;
}

View File

@ -342,7 +342,7 @@ private:
std::recursive_mutex mutex;
public:
VKTexture AllocateSurfaceTexture(SurfaceParams::SurfaceType type, vk::Format format,
void AllocateTexture(VKTexture& target, SurfaceParams::SurfaceType type, vk::Format format,
u32 width, u32 height);
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.
out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\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) {
out += "depth /= gl_FragCoord.w;\n";
}
@ -1655,11 +1656,14 @@ void main() {
texcoord0_w = vert_texcoord0_w;
normquat = vert_normquat;
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)
//gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
//gl_ClipDistance[1] = dot(clip_coef, vert_position);
#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
// Convert OpenGL [-1, 1] to Vulkan [0, 1]
vec4 position = vert_position;
position.z = (position.z + 1) * 0.5;
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/renderer_vulkan.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_shader_gen.h"
@ -82,7 +83,7 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
VKTexture::Info info{
.width = 1,
.height = 1,
.format = vk::Format::eR8G8B8A8Srgb,
.format = vk::Format::eR8G8B8A8Unorm,
.type = vk::ImageType::e2D,
.view_type = vk::ImageViewType::e2D,
.usage = vk::ImageUsageFlagBits::eSampled |
@ -124,9 +125,12 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
VulkanState::~VulkanState() {
auto device = g_vk_instace->GetDevice();
device.waitIdle();
// Destroy vertex shader
device.destroyShaderModule(render_vertex_shader);
device.destroyShaderModule(present_vertex_shader);
device.destroyShaderModule(present_fragment_shader);
// Destroy pipeline layouts
device.destroyPipelineLayout(render_pipeline_layout);
@ -227,7 +231,7 @@ void VulkanState::UnbindTexture(u32 unit) {
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::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear,
vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op,
@ -236,38 +240,37 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
EndRendering();
// 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{};
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
if (color.has_value()) {
auto& image = color->get();
image.Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
if (color != nullptr) {
color->Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
infos[0] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {},
color->GetView(), color->GetLayout(), {}, {}, {},
color_load_op, color_store_op, color_clear
};
render_info.colorAttachmentCount = 1;
render_info.pColorAttachments = &infos[0];
render_info.renderArea = color->GetArea();
}
if (depth.has_value()) {
auto& image = depth->get();
image.Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
if (depth != nullptr) {
depth->Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
infos[1] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {},
depth->GetView(), depth->GetLayout(), {}, {}, {},
depth_load_op, depth_store_op, depth_clear
};
render_info.pDepthAttachment = &infos[1];
if (IsStencil(image.GetFormat())) {
if (IsStencil(depth->GetFormat())) {
infos[2] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {},
depth->GetView(), depth->GetLayout(), {}, {}, {},
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) {
render_pipeline_key.color = color.has_value() ? color->get().GetFormat() : vk::Format::eUndefined;
render_pipeline_key.depth_stencil = depth.has_value() ? depth->get().GetFormat() : vk::Format::eUndefined;
render_pipeline_key.color = color != nullptr ?
color->GetFormat() :
vk::Format::eUndefined;
render_pipeline_key.depth_stencil = depth != nullptr ?
depth->GetFormat() :
vk::Format::eUndefined;
}
// Begin rendering

View File

@ -17,9 +17,6 @@ namespace Vulkan {
constexpr u32 DESCRIPTOR_SET_COUNT = 4;
template <typename T>
using OptRef = std::optional<std::reference_wrapper<T>>;
struct DrawInfo {
glm::mat4 modelview;
glm::vec4 i_resolution;
@ -93,7 +90,7 @@ public:
vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha);
/// 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::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad,
vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore,

View File

@ -19,9 +19,13 @@ VKSwapChain::VKSwapChain(vk::SurfaceKHR surface_) : surface(surface_) {
VKSwapChain::~VKSwapChain() {
auto device = g_vk_instace->GetDevice();
auto instance = g_vk_instace->GetInstance();
device.waitIdle();
device.destroySemaphore(render_finished);
device.destroySemaphore(image_available);
device.destroySwapchainKHR(swapchain);
instance.destroySurfaceKHR(surface);
}
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
if (!image_available) {
image_available = device.createSemaphoreUnique({});
image_available = device.createSemaphore({});
}
if (!render_finished) {
render_finished = device.createSemaphoreUnique({});
render_finished = device.createSemaphore({});
}
// Create framebuffer and image views
@ -78,7 +82,7 @@ constexpr u64 ACQUIRE_TIMEOUT = 1000000000;
void VKSwapChain::AcquireNextImage() {
auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT,
image_available.get(), VK_NULL_HANDLE,
image_available, VK_NULL_HANDLE,
&image_index);
switch (result) {
case vk::Result::eSuccess:
@ -98,7 +102,7 @@ void VKSwapChain::AcquireNextImage() {
void VKSwapChain::Present() {
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);
switch (result) {

View File

@ -42,8 +42,8 @@ public:
vk::SurfaceKHR GetSurface() const { return surface; }
vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; }
vk::SwapchainKHR GetSwapChain() const { return swapchain; }
const vk::Semaphore& GetAvailableSemaphore() const { return image_available.get(); }
const vk::Semaphore& GetRenderSemaphore() const { return render_finished.get(); }
const vk::Semaphore& GetAvailableSemaphore() const { return image_available; }
const vk::Semaphore& GetRenderSemaphore() const { return render_finished; }
VKTexture& GetCurrentImage() { return swapchain_images[image_index]; }
private:
@ -53,7 +53,7 @@ private:
private:
SwapChainDetails details{};
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};
vk::SwapchainKHR swapchain{VK_NULL_HANDLE};

View File

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

View File

@ -19,10 +19,10 @@ static int BytesPerPixel(vk::Format format) {
case vk::Format::eD32Sfloat:
case vk::Format::eB8G8R8A8Unorm:
case vk::Format::eR8G8B8A8Uint:
case vk::Format::eR8G8B8A8Srgb:
case vk::Format::eR8G8B8A8Unorm:
case vk::Format::eD24UnormS8Uint:
return 4;
case vk::Format::eR8G8B8Uint:
case vk::Format::eR8G8B8Unorm:
case vk::Format::eR8G8B8Srgb:
return 3;
case vk::Format::eR5G6B5UnormPack16:
@ -58,15 +58,41 @@ VKTexture::~VKTexture() {
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) {
auto device = g_vk_instace->GetDevice();
info = create_info;
// Emulate RGB8 format with RGBA8
is_rgb = false;
if (info.format == vk::Format::eR8G8B8Srgb) {
if (info.format == vk::Format::eR8G8B8Unorm) {
is_rgb = true;
info.format = vk::Format::eR8G8B8A8Srgb;
info.format = vk::Format::eR8G8B8A8Unorm;
}
is_d24s8 = false;
@ -136,7 +162,9 @@ void VKTexture::Destroy() {
auto& state = VulkanState::Get();
state.UnbindTexture(*this);
auto deleter = [this]() {
auto deleter = [texture = texture,
view = view,
memory = memory]() {
auto device = g_vk_instace->GetDevice();
if (texture) {
std::cout << "Surface destroyed!\n";
@ -154,7 +182,7 @@ void VKTexture::Destroy() {
// If the image was adopted (probably from the swapchain) then only
// destroy the view
if (adopted) {
g_vk_task_scheduler->Schedule([this](){
g_vk_task_scheduler->Schedule([view = view](){
auto device = g_vk_instace->GetDevice();
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());
}
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,
{aspect, level, layer, 1},
{region.offset.x, region.offset.y, 0},
{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(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(),
texture, vk::ImageLayout::eTransferDstOptimal,
copy_region);
texture, vk::ImageLayout::eTransferDstOptimal, region_count,
copy_regions.data());
// Prepare image for shader reads
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) {
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);
if (!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();
// Copy pixels to staging buffer
vk::BufferImageCopy download_region{
std::array<vk::BufferImageCopy, 2> copy_regions;
u32 region_count = 1;
copy_regions[0] = vk::BufferImageCopy{
offset, row_length, region.extent.height,
{aspect, level, layer, 1},
{region.offset.x, region.offset.y, 0},
{region.extent.width, region.extent.height, 1}
};
// Automatically convert RGB to RGBA
if (is_rgb) {
auto data = RGBAToRGB(memory);
std::memcpy(buffer, data.data(), data.size());
}
else if (is_d24s8) {
auto data = D32S8ToD24S8(memory);
std::memcpy(buffer, data.data(), data.size() * sizeof(data[0]));
}
else {
std::memcpy(buffer, memory.data(), memory.size());
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
@ -339,15 +379,27 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal,
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
// NOTE: This is really slow and should be reworked
g_vk_task_scheduler->Submit(true);
std::memcpy(memory.data(), buffer, memory.size_bytes());
// Restore layout
Transition(cmdbuffer, old_layout);
// Automatically convert RGB to RGBA
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>

View File

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