renderer_vulkan: Texture management fixes

This commit is contained in:
emufan
2022-06-15 22:34:02 +03:00
parent ed9000d0ec
commit d4b88ac158
13 changed files with 154 additions and 73 deletions

View File

@@ -48,14 +48,15 @@ void EmuThread::run() {
MicroProfileOnThreadCreate("EmuThread");
Frontend::ScopeAcquireContext scope(core_context);
/*emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources(
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);*/
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
emit HideLoadingScreen();
if (Core::System::GetInstance().frame_limiter.IsFrameAdvancing()) {
// Usually the loading screen is hidden after the first frame is drawn. In this case

View File

@@ -185,10 +185,12 @@ void RendererVulkan::PrepareRendertarget() {
if (color_fill.is_enabled) {
LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, screen_infos[i]);
} else {
auto extent = screen_infos[i].texture.GetArea().extent;
auto format = screen_infos[i].format;
if (extent.width != framebuffer.width || extent.height != framebuffer.height ||
format != framebuffer.color_format) {
auto [width, height] = screen_infos[i].texture.GetArea().extent;
u32 fwidth = framebuffer.width;
u32 fheight = framebuffer.height;
if (width != fwidth || height != fheight ||
screen_infos[i].format != framebuffer.color_format) {
// Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a
// performance problem.
@@ -271,6 +273,7 @@ void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
*/
void RendererVulkan::CreateVulkanObjects() {
clear_color = vk::ClearColorValue{std::array<float, 4>{Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f}};
clear_color = vk::ClearColorValue(std::array<float, 4>{1.0f, 0.0f, 0.0, 1.0f});
//filter_sampler.Create();
//ReloadSampler();
@@ -286,7 +289,7 @@ void RendererVulkan::CreateVulkanObjects() {
}
void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer) {
GPU::Regs::PixelFormat format = framebuffer.color_format;
screen.format = framebuffer.color_format;
VKTexture::Info texture_info{
.width = framebuffer.width,
@@ -298,7 +301,7 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::
vk::ImageUsageFlagBits::eSampled
};
switch (format) {
switch (screen.format) {
case GPU::Regs::PixelFormat::RGBA8:
texture_info.format = vk::Format::eR8G8B8A8Srgb;
break;
@@ -371,7 +374,9 @@ void RendererVulkan::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa
state.ApplyPresentState();
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
cmdbuffer.bindVertexBuffers(0, vertex_buffer.GetBuffer(), {0});
vk::DeviceSize offset = 0;
cmdbuffer.bindVertexBuffers(0, 1, &vertex_buffer.GetBuffer(), &offset);
cmdbuffer.draw(4, 1, 0, 0);
}
@@ -716,9 +721,9 @@ VideoCore::ResultStatus RendererVulkan::Init() {
g_vk_instace->Create(instance, physical_devices[1], surface, true);
g_vk_task_scheduler->Create();
auto& layout = render_window.GetFramebufferLayout();
//auto& layout = render_window.GetFramebufferLayout();
swapchain = std::make_shared<VKSwapChain>(surface);
swapchain->Create(layout.width, layout.height, false);
//swapchain->Create(layout.width, layout.height, false);
// Create Vulkan state
VulkanState::Create(swapchain);

View File

@@ -158,6 +158,9 @@ void StreamBuffer::Commit(u32 size, vk::AccessFlags access_to_block,
buffer_pos += size;
}
else {
printf("f");
}
}
}

View File

@@ -44,7 +44,7 @@ public:
/// Return a pointer to the mapped memory if the buffer is host mapped
u8* GetHostPointer() const { return reinterpret_cast<u8*>(host_ptr); }
const vk::BufferView& GetView(u32 i = 0) const { return views[i]; }
vk::Buffer GetBuffer() const { return buffer; }
const vk::Buffer& GetBuffer() const { return buffer; }
u32 GetSize() const { return buffer_info.size; }
void Upload(std::span<const std::byte> data, u32 offset,

View File

@@ -245,7 +245,7 @@ void PipelineBuilder::SetRenderingFormats(vk::Format color, vk::Format depth_ste
};
const u32 color_attachment_count = color == vk::Format::eUndefined ? 0 : 1;
rendering_info = vk::PipelineRenderingCreateInfoKHR{0, color_attachment_count, &color_format, depth_stencil_format,
rendering_info = vk::PipelineRenderingCreateInfo{0, color_attachment_count, &color_format, depth_stencil_format,
IsStencil(depth_stencil) ? depth_stencil : vk::Format::eUndefined};
pipeline_info.pNext = &rendering_info;
}

View File

@@ -69,7 +69,7 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window) {
VKBuffer::Info texel_buffer_info = {
.size = TEXTURE_BUFFER_SIZE,
.properties = vk::MemoryPropertyFlagBits::eDeviceLocal,
.usage = vk::BufferUsageFlagBits::eStorageTexelBuffer |
.usage = vk::BufferUsageFlagBits::eUniformTexelBuffer |
vk::BufferUsageFlagBits::eTransferDst,
};
@@ -116,13 +116,18 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window) {
index_buffer.Create(index_info);
// Set clear texture color
state.SetPlaceholderColor(0, 0, 0, 255);
state.SetPlaceholderColor(255, 0, 0, 255);
SyncEntireState();
}
RasterizerVulkan::~RasterizerVulkan() = default;
void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
}
void RasterizerVulkan::SyncEntireState() {
// Sync fixed function Vulkan state
SyncClipEnabled();
@@ -394,8 +399,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
}
state.EndRendering();
color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
g_vk_task_scheduler->Submit();
auto gpu_tick = g_vk_task_scheduler->GetGPUTick();
auto cpu_tick = g_vk_task_scheduler->GetCPUTick();
return true;
}

View File

@@ -117,7 +117,7 @@ public:
~RasterizerVulkan() override;
void LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override {}
const VideoCore::DiskResourceLoadCallback& callback) override;
void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1,
const Pica::Shader::OutputVertex& v2) override;

View File

@@ -7,6 +7,7 @@
#include <bitset>
#include <cmath>
#include <cstring>
#include <iostream>
#include <iterator>
#include <optional>
#include <unordered_set>
@@ -428,6 +429,7 @@ void RasterizerCacheVulkan::CopySurface(const Surface& src_surface, const Surfac
MICROPROFILE_DEFINE(Vulkan_SurfaceLoad, "Vulkan", "Surface Load", MP_RGB(128, 192, 64));
void CachedSurface::LoadGPUBuffer(PAddr load_start, PAddr load_end) {
ASSERT(type != SurfaceType::Fill);
const bool need_swap = (pixel_format == PixelFormat::RGBA8 || pixel_format == PixelFormat::RGB8);
const u8* const texture_src_data = VideoCore::g_memory->GetPhysicalPointer(addr);
if (texture_src_data == nullptr)
@@ -451,8 +453,27 @@ void CachedSurface::LoadGPUBuffer(PAddr load_start, PAddr load_end) {
if (!is_tiled) {
ASSERT(type == SurfaceType::Color);
if (need_swap) {
// TODO(liushuyu): check if the byteswap here is 100% correct
// cannot fully test this
if (pixel_format == PixelFormat::RGBA8) {
for (std::size_t i = start_offset; i < load_end - addr; i += 4) {
vk_buffer[i] = texture_src_data[i + 3];
vk_buffer[i + 1] = texture_src_data[i + 2];
vk_buffer[i + 2] = texture_src_data[i + 1];
vk_buffer[i + 3] = texture_src_data[i];
}
} else if (pixel_format == PixelFormat::RGB8) {
for (std::size_t i = start_offset; i < load_end - addr; i += 3) {
vk_buffer[i] = texture_src_data[i + 2];
vk_buffer[i + 1] = texture_src_data[i + 1];
vk_buffer[i + 2] = texture_src_data[i];
}
}
} else {
std::memcpy(&vk_buffer[start_offset], texture_src_data + start_offset,
load_end - load_start);
}
} else {
if (type == SurfaceType::Texture) {
Pica::Texture::TextureInfo tex_info{};

View File

@@ -1677,7 +1677,7 @@ vk::ShaderModule CompileShader(const std::string& source, vk::ShaderStageFlagBit
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.SetOptimizationLevel(shaderc_optimization_level_performance);
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1);
options.SetWarningsAsErrors();
options.SetSourceLanguage(shaderc_source_language_glsl);

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <span>
#include "video_core/renderer_vulkan/vk_state.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
@@ -13,6 +14,17 @@ namespace Vulkan {
std::unique_ptr<VulkanState> s_vulkan_state{};
auto IsStencil = [](vk::Format format) -> bool {
switch (format) {
case vk::Format::eD16UnormS8Uint:
case vk::Format::eD24UnormS8Uint:
case vk::Format::eD32SfloatS8Uint:
return true;
default:
return false;
};
};
void DescriptorUpdater::Update() {
assert(update_count > 0);
@@ -151,7 +163,7 @@ void VulkanState::SetTexture(u32 binding, const VKTexture& image) {
void VulkanState::SetTexelBuffer(u32 binding, u32 offset, u32 size, const VKBuffer& buffer, u32 view_index) {
auto& set = descriptor_sets[2];
updater.PushBufferUpdate(set, binding,
vk::DescriptorType::eStorageTexelBuffer,
vk::DescriptorType::eUniformTexelBuffer,
offset, size, buffer.GetBuffer(),
buffer.GetView(view_index));
descriptors_dirty = true;
@@ -177,18 +189,24 @@ void VulkanState::UnbindTexture(const VKTexture& image) {
for (int i = 0; i < 4; i++) {
if (render_views[i] == image.GetView()) {
render_views[i] = placeholder.GetView();
updater.PushCombinedImageSamplerUpdate(descriptor_sets[1], i,
render_sampler, render_views[i]);
descriptors_dirty = true;
}
}
if (present_view == image.GetView()) {
present_view = placeholder.GetView();
updater.PushCombinedImageSamplerUpdate(descriptor_sets[3], 0,
render_sampler, present_view);
descriptors_dirty = true;
}
}
void VulkanState::UnbindTexture(u32 unit) {
render_views[unit] = placeholder.GetView();
updater.PushCombinedImageSamplerUpdate(descriptor_sets[1], unit,
render_sampler, render_views[unit]);
descriptors_dirty = true;
}
@@ -227,14 +245,18 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
depth_load_op, depth_store_op, depth_clear
};
render_info.pDepthAttachment = &infos[1];
if (IsStencil(image.GetFormat())) {
infos[2] = vk::RenderingAttachmentInfo{
image.GetView(), image.GetLayout(), {}, {}, {},
stencil_load_op, stencil_store_op, depth_clear
};
render_info.pDepthAttachment = &infos[1];
render_info.pStencilAttachment = &infos[2];
}
}
if (update_pipeline_formats) {
render_pipeline_key.color = color.has_value() ? color->get().GetFormat() : vk::Format::eUndefined;
@@ -363,25 +385,26 @@ void VulkanState::InitDescriptorSets() {
auto sets = device.allocateDescriptorSets(allocate_info);
// Update them if the previous sets are valid
auto result = std::ranges::find_if(descriptor_sets, [](vk::DescriptorSet set) { return bool(set); });
if (result != descriptor_sets.end()) {
std::array<vk::CopyDescriptorSet, 10> copies{{
{descriptor_sets[0], 0, 0, sets[0], 0, 0}, // shader_data
{descriptor_sets[0], 1, 0, sets[0], 1, 0}, // pica_uniforms
{descriptor_sets[1], 0, 0, sets[1], 0, 0}, // tex0
{descriptor_sets[1], 1, 0, sets[1], 1, 0}, // tex1
{descriptor_sets[1], 2, 0, sets[1], 2, 0}, // tex2
{descriptor_sets[1], 3, 0, sets[1], 3, 0}, // tex_cube
{descriptor_sets[2], 0, 0, sets[2], 0, 0}, // texture_buffer_lut_lf
{descriptor_sets[2], 1, 0, sets[2], 1, 0}, // texture_buffer_lut_rg
{descriptor_sets[2], 2, 0, sets[2], 2, 0}, // texture_buffer_lut_rgba
{descriptor_sets[3], 0, 0, sets[3], 0, 0}
}};
u32 copy_count = 0;
std::array<vk::CopyDescriptorSet, 10> copies;
device.updateDescriptorSets({}, copies);
// Copy only valid descriptors
std::array<u32, 4> binding_count{2, 4, 3, 1};
for (int i = 0; i < descriptor_sets.size(); i++) {
if (descriptor_sets[i]) {
for (u32 binding = 0; binding < binding_count[i]; binding++) {
copies[copy_count++] = {descriptor_sets[i], binding, 0, sets[i], binding, 0, 1};
}
}
}
std::copy_n(sets.begin(), 4, descriptor_sets.begin());
if (copy_count < 10) {
// Some descriptors weren't copied and thus need manual updating
descriptors_dirty = true;
}
device.updateDescriptorSets(0, nullptr, copy_count, copies.data());
std::copy_n(sets.begin(), descriptor_sets.size(), descriptor_sets.begin());
}
void VulkanState::ApplyRenderState(const Pica::Regs& regs) {
@@ -533,9 +556,9 @@ void VulkanState::BuildDescriptorLayouts() {
{3, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, // tex_cube
}};
std::array<vk::DescriptorSetLayoutBinding, 3> lut_set{{
{0, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, // texture_buffer_lut_lf
{1, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, // texture_buffer_lut_rg
{2, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment} // texture_buffer_lut_rgba
{0, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, // texture_buffer_lut_lf
{1, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, // texture_buffer_lut_rg
{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}
@@ -579,7 +602,7 @@ void VulkanState::ConfigureRenderPipeline() {
// Enable every required dynamic state
std::array dynamic_states{
vk::DynamicState::eDepthCompareOp, vk::DynamicState::eLineWidth,
vk::DynamicState::eDepthCompareOp,
vk::DynamicState::eDepthTestEnable, vk::DynamicState::eStencilTestEnable,
vk::DynamicState::eStencilOp,
vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask,
@@ -614,7 +637,7 @@ void VulkanState::ConfigurePresentPipeline() {
present_pipeline_builder.SetPrimitiveTopology(vk::PrimitiveTopology::eTriangleStrip);
present_pipeline_builder.SetLineWidth(1.0f);
present_pipeline_builder.SetNoCullRasterizationState();
present_pipeline_builder.SetRenderingFormats(swapchain->GetCurrentImage().GetFormat());
present_pipeline_builder.SetRenderingFormats(vk::Format::eB8G8R8A8Unorm);
// Set depth, stencil tests and blending
present_pipeline_builder.SetNoDepthTestState();
@@ -623,7 +646,6 @@ void VulkanState::ConfigurePresentPipeline() {
// Enable every required dynamic state
std::array dynamic_states{
vk::DynamicState::eLineWidth,
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};

View File

@@ -125,8 +125,7 @@ private:
private:
// Render targets
std::shared_ptr<VKSwapChain> swapchain;
bool rendering{};
VKTexture* color_render_target{}, *depth_render_target{};
bool rendering{false};
vk::ImageView present_view;
std::array<vk::ImageView, 4> render_views;
DrawInfo present_data;

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fstream>
#include <iostream>
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/vk_texture.h"
@@ -26,6 +28,7 @@ static int BytesPerPixel(vk::Format format) {
case vk::Format::eR5G6B5UnormPack16:
case vk::Format::eR5G5B5A1UnormPack16:
case vk::Format::eR4G4B4A4UnormPack16:
case vk::Format::eD16Unorm:
return 2;
default:
UNREACHABLE();
@@ -247,7 +250,9 @@ void VKTexture::OverrideImageLayout(vk::ImageLayout new_layout) {
}
void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> pixels) {
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(pixels.size_bytes());
u32 request_size = is_rgb ? (pixels.size() / 3) * 4 :
(is_d24s8 ? (pixels.size() / 4) * 5 : pixels.size());
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(request_size);
if (!buffer) {
LOG_ERROR(Render_Vulkan, "Cannot upload pixels without staging buffer!");
}
@@ -271,8 +276,6 @@ 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{
offset, row_length, region.extent.height,
{aspect, level, layer, 1},
@@ -342,33 +345,44 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
Transition(cmdbuffer, old_layout);
}
template <typename Out, typename In>
std::span<Out> SpanCast(std::span<In> span) {
return std::span(reinterpret_cast<Out*>(span.data()), span.size_bytes() / sizeof(Out));
}
std::vector<u8> VKTexture::RGBToRGBA(std::span<u8> data) {
ASSERT(data.size() % 3 == 0);
u32 new_size = (data.size() / 3) * 4;
std::vector<u8> rgba(new_size, 255);
std::vector<u8> rgba(new_size);
u32 dst_pos = 0;
for (u32 i = 0; i < data.size(); i += 3) {
std::memcpy(rgba.data() + dst_pos, data.data() + i, 3);
rgba[dst_pos + 3] = 255u;
dst_pos += 4;
}
return rgba;
}
std::vector<u8> VKTexture::D24S8ToD32S8(std::span<u8> data) {
std::vector<u64> VKTexture::D24S8ToD32S8(std::span<u8> data) {
ASSERT(data.size() % 4 == 0);
u32 new_size = (data.size() / 4) * 8;
std::vector<u8> d32s8(new_size, 0);
std::vector<u64> d32s8;
std::span<u32> d24s8 = SpanCast<u32>(data);
u32 dst_pos = 0;
for (u32 i = 0; i < data.size(); i += 4) {
std::memcpy(d32s8.data() + dst_pos, data.data() + i, 3);
d32s8[dst_pos + 4] = data[i + 3];
dst_pos += 8;
}
d32s8.reserve(data.size() * 2);
std::ranges::transform(d24s8, std::back_inserter(d32s8), [](u32 comp) -> u64 {
// Convert normalized 24bit depth component to floating point
float fdepth = static_cast<float>(comp & 0xFFFFFF) / 0xFFFFFF;
u64 result = static_cast<u64>(comp) << 8;
// Use std::memcpy to avoid the unsafe casting required to preserve the floating
// point bits
std::memcpy(&result, &fdepth, 4);
return result;
});
return d32s8;
}
@@ -388,18 +402,23 @@ std::vector<u8> VKTexture::RGBAToRGB(std::span<u8> data) {
return rgb;
}
std::vector<u8> VKTexture::D32S8ToD24S8(std::span<u8> data) {
std::vector<u32> VKTexture::D32S8ToD24S8(std::span<u8> data) {
ASSERT(data.size() % 8 == 0);
u32 new_size = (data.size() / 8) * 4;
std::vector<u8> d24s8(new_size);
std::vector<u32> d24s8;
std::span<u64> d32s8 = SpanCast<u64>(data);
u32 dst_pos = 0;
for (u32 i = 0; i < data.size(); i += 5) {
std::memcpy(d24s8.data() + dst_pos, data.data() + i, 3);
d24s8[dst_pos + 3] = data[i + 4];
dst_pos += 4;
}
d24s8.reserve(data.size() / 2);
std::ranges::transform(d32s8, std::back_inserter(d24s8), [](u64 comp) -> u32 {
// Convert floating point to 24bit normalized depth
float fdepth = 0.f;
u32 depth = comp & 0xFFFFFFFF;
std::memcpy(&fdepth, &depth, 4);
u32 stencil = (comp >> 32) & 0xFF;
u64 result = static_cast<u32>(fdepth * 0xFFFFFF) | (stencil << 24);
return result;
});
return d24s8;
}

View File

@@ -62,10 +62,10 @@ public:
private:
std::vector<u8> RGBToRGBA(std::span<u8> data);
std::vector<u8> D24S8ToD32S8(std::span<u8> data);
std::vector<u64> D24S8ToD32S8(std::span<u8> data);
std::vector<u8> RGBAToRGB(std::span<u8> data);
std::vector<u8> D32S8ToD24S8(std::span<u8> data);
std::vector<u32> D32S8ToD24S8(std::span<u8> data);
private:
VKTexture::Info info{};