vk_rasterizer: Small cleanup

This commit is contained in:
GPUCode
2023-02-09 15:51:31 +02:00
parent 3fe0130fdb
commit d461778296
5 changed files with 248 additions and 227 deletions

View File

@@ -450,12 +450,21 @@ void RasterizerVulkan::DrawTriangles() {
return;
}
const u64 vertex_size = vertex_batch.size() * sizeof(HardwareVertex);
pipeline_info.rasterization.topology.Assign(Pica::PipelineRegs::TriangleTopology::List);
pipeline_info.vertex_layout = software_layout;
pipeline_cache.UseTrivialVertexShader();
pipeline_cache.UseTrivialGeometryShader();
auto [buffer, offset, _] = stream_buffer.Map(vertex_size, sizeof(HardwareVertex));
std::memcpy(buffer, vertex_batch.data(), vertex_size);
stream_buffer.Commit(vertex_size);
scheduler.Record([this, offset = offset](vk::CommandBuffer cmdbuf) {
cmdbuf.bindVertexBuffers(0, stream_buffer.Handle(), offset);
});
Draw(false, false);
}
@@ -537,45 +546,79 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
uniform_block_data.dirty = true;
}
const auto BindCubeFace = [&](Pica::TexturingRegs::CubeFace face,
Pica::Texture::TextureInfo& info) {
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
auto surface = res_cache.GetTextureSurface(info);
const u32 binding = static_cast<u32>(face);
if (surface) {
pipeline_cache.BindStorageImage(binding, surface->ImageView());
} else {
pipeline_cache.BindStorageImage(binding, null_storage_surface.ImageView());
}
};
const auto BindSampler = [&](u32 binding, SamplerInfo& info,
const Pica::TexturingRegs::TextureConfig& config) {
// TODO(GPUCode): Cubemaps don't contain any mipmaps for now, so sampling from them returns
// nothing. Always sample from the base level until mipmaps for texture cubes are
// implemented
const bool skip_mipmap = config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
info = SamplerInfo{.mag_filter = config.mag_filter,
.min_filter = config.min_filter,
.mip_filter = config.mip_filter,
.wrap_s = config.wrap_s,
.wrap_t = config.wrap_t,
.border_color = config.border_color.raw,
.lod_min = skip_mipmap ? 0.f : static_cast<float>(config.lod.min_level),
.lod_max = skip_mipmap ? 0.f : static_cast<float>(config.lod.max_level)};
// Search the cache and bind the appropriate sampler
if (auto it = samplers.find(info); it != samplers.end()) {
pipeline_cache.BindSampler(binding, it->second);
} else {
vk::Sampler texture_sampler = CreateSampler(info);
samplers.emplace(info, texture_sampler);
pipeline_cache.BindSampler(binding, texture_sampler);
}
};
// Sync and bind the texture surfaces
// NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will
// cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state
SyncTextureUnits(color_surface.get());
const vk::Rect2D render_area = {
.offset{static_cast<s32>(draw_rect.left), static_cast<s32>(draw_rect.bottom)},
.extent{draw_rect.GetWidth(), draw_rect.GetHeight()},
};
renderpass_cache.EnterRenderpass(color_surface.get(), depth_surface.get(), render_area);
// Sync and bind the shader
if (shader_dirty) {
pipeline_cache.UseFragmentShader(regs);
shader_dirty = false;
}
// Sync the LUTs within the texture buffer
SyncAndUploadLUTs();
SyncAndUploadLUTsLF();
// Sync the uniform data
UploadUniforms(accelerate);
// Sync the viewport
pipeline_cache.SetViewport(surfaces_rect.left + viewport_rect_unscaled.left * res_scale,
surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale,
viewport_rect_unscaled.GetWidth() * res_scale,
viewport_rect_unscaled.GetHeight() * res_scale);
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
// Enable scissor test to prevent drawing outside of the framebuffer region
pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(),
draw_rect.GetHeight());
// Draw the vertex batch
bool succeeded = true;
if (accelerate) {
succeeded = AccelerateDrawBatchInternal(is_indexed);
} else {
pipeline_cache.BindPipeline(pipeline_info, true);
scheduler.Record([vertex_count = vertex_batch.size()](vk::CommandBuffer cmdbuf) {
cmdbuf.draw(vertex_count, 1, 0, 0);
});
}
vertex_batch.clear();
// Mark framebuffer surfaces as dirty
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
if (color_surface && write_color_fb) {
auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
color_surface);
}
if (depth_surface && write_depth_fb) {
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
depth_surface);
}
static int counter = 20;
counter--;
if (counter == 0) {
scheduler.DispatchWork();
counter = 20;
}
return succeeded;
}
void RasterizerVulkan::SyncTextureUnits(Surface* const color_surface) {
const auto pica_textures = regs.texturing.GetTextures();
for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
const auto& texture = pica_textures[texture_index];
@@ -597,12 +640,20 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
using CubeFace = Pica::TexturingRegs::CubeFace;
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config,
texture.format);
BindCubeFace(CubeFace::PositiveX, info);
BindCubeFace(CubeFace::NegativeX, info);
BindCubeFace(CubeFace::PositiveY, info);
BindCubeFace(CubeFace::NegativeY, info);
BindCubeFace(CubeFace::PositiveZ, info);
BindCubeFace(CubeFace::NegativeZ, info);
for (CubeFace face :
{CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY,
CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ}) {
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
auto surface = res_cache.GetTextureSurface(info);
const u32 binding = static_cast<u32>(face);
if (surface) {
pipeline_cache.BindStorageImage(binding, surface->ImageView());
} else {
pipeline_cache.BindStorageImage(binding,
null_storage_surface.ImageView());
}
}
continue;
}
case TextureType::TextureCube: {
@@ -669,96 +720,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
pipeline_cache.BindSampler(texture_index, default_sampler);
}
}
// NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will
// cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state
const vk::Rect2D render_area = {
.offset{
.x = static_cast<s32>(draw_rect.left),
.y = static_cast<s32>(draw_rect.bottom),
},
.extent{
.width = draw_rect.GetWidth(),
.height = draw_rect.GetHeight(),
},
};
renderpass_cache.EnterRenderpass(color_surface.get(), depth_surface.get(), render_area);
// Sync and bind the shader
if (shader_dirty) {
pipeline_cache.UseFragmentShader(regs);
shader_dirty = false;
}
// Sync the LUTs within the texture buffer
SyncAndUploadLUTs();
SyncAndUploadLUTsLF();
// Sync the uniform data
UploadUniforms(accelerate);
// Sync the viewport
pipeline_cache.SetViewport(surfaces_rect.left + viewport_rect_unscaled.left * res_scale,
surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale,
viewport_rect_unscaled.GetWidth() * res_scale,
viewport_rect_unscaled.GetHeight() * res_scale);
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
// Enable scissor test to prevent drawing outside of the framebuffer region
pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(),
draw_rect.GetHeight());
// Draw the vertex batch
bool succeeded = true;
if (accelerate) {
succeeded = AccelerateDrawBatchInternal(is_indexed);
} else {
pipeline_cache.BindPipeline(pipeline_info, true);
const u32 max_vertices = STREAM_BUFFER_SIZE / sizeof(HardwareVertex);
const u32 batch_size = static_cast<u32>(vertex_batch.size());
for (u32 base_vertex = 0; base_vertex < batch_size; base_vertex += max_vertices) {
const u32 vertices = std::min(max_vertices, batch_size - base_vertex);
const u32 vertex_size = vertices * sizeof(HardwareVertex);
// Copy vertex data
auto [array_ptr, offset, _] = stream_buffer.Map(vertex_size, sizeof(HardwareVertex));
std::memcpy(array_ptr, vertex_batch.data() + base_vertex, vertex_size);
stream_buffer.Commit(vertex_size);
scheduler.Record(
[this, vertices, base_vertex, offset = offset](vk::CommandBuffer cmdbuf) {
cmdbuf.bindVertexBuffers(0, stream_buffer.Handle(), offset);
cmdbuf.draw(vertices, 1, base_vertex, 0);
});
}
}
vertex_batch.clear();
// Mark framebuffer surfaces as dirty
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
if (color_surface && write_color_fb) {
auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
color_surface);
}
if (depth_surface && write_depth_fb) {
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
depth_surface);
}
static int counter = 20;
counter--;
if (counter == 0) {
scheduler.DispatchWork();
counter = 20;
}
return succeeded;
}
void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
@@ -1045,6 +1006,30 @@ void RasterizerVulkan::MakeSoftwareVertexLayout() {
}
}
void RasterizerVulkan::BindSampler(u32 unit, SamplerInfo& info,
const Pica::TexturingRegs::TextureConfig& config) {
// TODO: Cubemaps don't contain any mipmaps for now, so sampling from them returns
// nothing. Always sample from the base level until mipmaps for texture cubes are
// implemented
const bool skip_mipmap = config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
info = SamplerInfo{
.mag_filter = config.mag_filter,
.min_filter = config.min_filter,
.mip_filter = config.mip_filter,
.wrap_s = config.wrap_s,
.wrap_t = config.wrap_t,
.border_color = config.border_color.raw,
.lod_min = skip_mipmap ? 0.f : static_cast<float>(config.lod.min_level),
.lod_max = skip_mipmap ? 0.f : static_cast<float>(config.lod.max_level),
};
auto [it, new_sampler] = samplers.try_emplace(info);
if (new_sampler) {
it->second = CreateSampler(info);
}
pipeline_cache.BindSampler(unit, it->second);
}
vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& info) {
const bool use_border_color = instance.IsCustomBorderColorSupported() &&
(info.wrap_s == SamplerInfo::TextureConfig::ClampToBorder ||

View File

@@ -122,6 +122,9 @@ private:
void SyncAndUploadLUTs();
void SyncAndUploadLUTsLF();
/// Syncs all enabled PICA texture units
void SyncTextureUnits(Surface* const color_surface);
/// Upload the uniform blocks to the uniform buffer object
void UploadUniforms(bool accelerate_draw);
@@ -149,6 +152,9 @@ private:
/// Creates the vertex layout struct used for software shader pipelines
void MakeSoftwareVertexLayout();
/// Binds a sampler to the specified texture unit
void BindSampler(u32 unit, SamplerInfo& info, const Pica::TexturingRegs::TextureConfig& config);
/// Creates a new sampler object
vk::Sampler CreateSampler(const SamplerInfo& info);

View File

@@ -11,6 +11,7 @@
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
@@ -98,6 +99,7 @@ public:
void Wait(u64 tick) {
if (tick >= master_semaphore.CurrentTick()) {
// Make sure we are not waiting for the current tick without signalling
LOG_WARNING(Render_Vulkan, "Flushing current tick");
Flush();
}
master_semaphore.Wait(tick);

View File

@@ -19,6 +19,8 @@ MICROPROFILE_DEFINE(Vulkan_Download, "Vulkan", "Texture Download", MP_RGB(128, 1
namespace Vulkan {
using VideoCore::PixelFormatAsString;
struct RecordParams {
vk::ImageAspectFlags aspect;
vk::Filter filter;
@@ -127,10 +129,10 @@ TextureRuntime::~TextureRuntime() {
for (const auto& [key, alloc] : texture_recycler) {
vmaDestroyImage(allocator, alloc.image, alloc.allocation);
device.destroyImageView(alloc.image_view);
if (alloc.base_view) {
if (alloc.base_view && alloc.base_view != alloc.image_view) {
device.destroyImageView(alloc.base_view);
}
device.destroyImageView(alloc.image_view);
if (alloc.depth_view) {
device.destroyImageView(alloc.depth_view);
device.destroyImageView(alloc.stencil_view);
@@ -264,59 +266,8 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
vk::Device device = instance.GetDevice();
alloc.image_view = device.createImageView(view_info);
// Also create a base mip view in case this is used as an attachment
if (levels > 1) [[likely]] {
const vk::ImageViewCreateInfo base_view_info = {
.image = alloc.image,
.viewType = view_type,
.format = format,
.subresourceRange{
.aspectMask = alloc.aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = layers,
},
};
alloc.base_view = device.createImageView(base_view_info);
}
// Create seperate depth/stencil views in case this gets reinterpreted with a compute shader
if (alloc.aspect & vk::ImageAspectFlagBits::eStencil) {
vk::ImageViewCreateInfo view_info = {
.image = alloc.image,
.viewType = view_type,
.format = format,
.subresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = levels,
.baseArrayLayer = 0,
.layerCount = layers,
},
};
alloc.depth_view = device.createImageView(view_info);
view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eStencil;
alloc.stencil_view = device.createImageView(view_info);
}
if (create_storage_view) {
const vk::ImageViewCreateInfo storage_view_info = {
.image = alloc.image,
.viewType = view_type,
.format = vk::Format::eR32Uint,
.subresourceRange{
.aspectMask = alloc.aspect,
.baseMipLevel = 0,
.levelCount = levels,
.baseArrayLayer = 0,
.layerCount = layers,
},
};
alloc.storage_view = device.createImageView(storage_view_info);
if (levels == 1) {
alloc.base_view = alloc.image_view;
}
renderpass_cache.ExitRenderpass();
@@ -1051,6 +1002,100 @@ vk::PipelineStageFlags Surface::PipelineStageFlags() const noexcept {
: vk::PipelineStageFlagBits::eNone);
}
vk::ImageView Surface::FramebufferView() noexcept {
vk::ImageView& base_view = alloc.base_view;
if (base_view) {
return base_view;
}
const vk::ImageViewCreateInfo base_view_info = {
.image = alloc.image,
.viewType = vk::ImageViewType::e2D,
.format = instance.GetTraits(pixel_format).native,
.subresourceRange{
.aspectMask = alloc.aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
base_view = instance.GetDevice().createImageView(base_view_info);
return base_view;
}
vk::ImageView Surface::DepthView() noexcept {
vk::ImageView& depth_view = alloc.depth_view;
if (depth_view) {
return depth_view;
}
const vk::ImageViewCreateInfo view_info = {
.image = alloc.image,
.viewType = vk::ImageViewType::e2D,
.format = instance.GetTraits(pixel_format).native,
.subresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
depth_view = instance.GetDevice().createImageView(view_info);
return depth_view;
}
vk::ImageView Surface::StencilView() noexcept {
vk::ImageView& stencil_view = alloc.stencil_view;
if (stencil_view) {
return stencil_view;
}
const vk::ImageViewCreateInfo view_info = {
.image = alloc.image,
.viewType = vk::ImageViewType::e2D,
.format = instance.GetTraits(pixel_format).native,
.subresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eStencil,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
stencil_view = instance.GetDevice().createImageView(view_info);
return stencil_view;
}
vk::ImageView Surface::StorageView() noexcept {
vk::ImageView& storage_view = alloc.storage_view;
if (storage_view) {
return storage_view;
}
ASSERT_MSG(pixel_format == VideoCore::PixelFormat::RGBA8,
"Attempted to retrieve storage view from unsupported surface with format {}",
PixelFormatAsString(pixel_format));
const vk::ImageViewCreateInfo storage_view_info = {
.image = alloc.image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eR32Uint,
.subresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
storage_view = instance.GetDevice().createImageView(storage_view_info);
return storage_view;
}
void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) {
const u32 rect_width = upload.texture_rect.GetWidth();
const u32 rect_height = upload.texture_rect.GetHeight();

View File

@@ -170,21 +170,6 @@ public:
vk::ImageAspectFlags aspect, TextureRuntime& runtime);
~Surface() override;
/// Uploads pixel data in staging to a rectangle region of the surface texture
void Upload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging);
/// Downloads pixel data to staging from a rectangle region of the surface texture
void Download(const VideoCore::BufferTextureCopy& download, const StagingData& staging);
/// Returns the bpp of the internal surface format
u32 GetInternalBytesPerPixel() const;
/// Returns the access flags indicative of the surface
vk::AccessFlags AccessFlags() const noexcept;
/// Returns the pipeline stage flags indicative of the surface
vk::PipelineStageFlags PipelineStageFlags() const noexcept;
/// Returns the surface aspect
vk::ImageAspectFlags Aspect() const noexcept {
return alloc.aspect;
@@ -200,34 +185,32 @@ public:
return alloc.image_view;
}
/// Uploads pixel data in staging to a rectangle region of the surface texture
void Upload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging);
/// Downloads pixel data to staging from a rectangle region of the surface texture
void Download(const VideoCore::BufferTextureCopy& download, const StagingData& staging);
/// Returns the bpp of the internal surface format
u32 GetInternalBytesPerPixel() const;
/// Returns the access flags indicative of the surface
vk::AccessFlags AccessFlags() const noexcept;
/// Returns the pipeline stage flags indicative of the surface
vk::PipelineStageFlags PipelineStageFlags() const noexcept;
/// Returns an image view used to create a framebuffer
vk::ImageView FramebufferView() noexcept {
is_framebuffer = true;
return alloc.base_view;
}
vk::ImageView FramebufferView() noexcept;
/// Returns the depth only image view of the surface, null otherwise
vk::ImageView DepthView() const noexcept {
return alloc.depth_view;
}
/// Returns the depth only image view of the surface
vk::ImageView DepthView() noexcept;
/// Returns the stencil only image view of the surface, null otherwise
vk::ImageView StencilView() const noexcept {
return alloc.stencil_view;
}
/// Returns the stencil only image view of the surface
vk::ImageView StencilView() noexcept;
/// Returns the R32 image view used for atomic load/store
vk::ImageView StorageView() noexcept {
if (!alloc.storage_view) {
LOG_CRITICAL(Render_Vulkan,
"Surface with pixel format {} and internal format {} "
"does not provide requested storage view!",
VideoCore::PixelFormatAsString(pixel_format), vk::to_string(alloc.format));
UNREACHABLE();
}
is_storage = true;
return alloc.storage_view;
}
vk::ImageView StorageView() noexcept;
private:
/// Uploads pixel data to scaled texture