rasterizer_cache: Remove usage of shared_ptr

This commit is contained in:
GPUCode
2023-02-27 18:37:39 +02:00
parent 42bed30b98
commit 06caa535d6
21 changed files with 777 additions and 749 deletions

View File

@ -143,7 +143,7 @@ protected:
Memory::MemorySystem& memory;
Pica::Regs& regs;
VertexArrayInfo vertex_info;
VertexArrayInfo vertex_info{};
std::vector<HardwareVertex> vertex_batch;
bool shader_dirty = true;

View File

@ -10,10 +10,9 @@ namespace VideoCore {
FramebufferBase::FramebufferBase() = default;
FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color_,
SurfaceBase* const depth_stencil_,
Common::Rectangle<u32> surfaces_rect)
: color{color_}, depth_stencil{depth_stencil_} {
FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color,
SurfaceBase* const depth_stencil,
Common::Rectangle<u32> surfaces_rect) {
res_scale = color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u);
// Determine the draw rectangle (render area + scissor)
@ -54,9 +53,11 @@ FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const colo
// Query surface invalidation intervals
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
if (color) {
color_params = *color;
intervals[0] = color->GetSubRectInterval(draw_rect_unscaled);
}
if (depth_stencil) {
depth_params = *depth_stencil;
intervals[1] = depth_stencil->GetSubRectInterval(draw_rect_unscaled);
}
}

View File

@ -5,7 +5,7 @@
#pragma once
#include "common/math_util.h"
#include "video_core/rasterizer_cache/utils.h"
#include "video_core/rasterizer_cache/surface_params.h"
namespace Pica {
struct Regs;
@ -32,12 +32,12 @@ public:
FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color,
SurfaceBase* const depth_stencil, Common::Rectangle<u32> surfaces_rect);
SurfaceBase* Color() const noexcept {
return color;
SurfaceParams ColorParams() const noexcept {
return color_params;
}
SurfaceBase* DepthStencil() const noexcept {
return depth_stencil;
SurfaceParams DepthParams() const noexcept {
return depth_params;
}
SurfaceInterval Interval(SurfaceType type) const noexcept {
@ -74,8 +74,8 @@ protected:
}
protected:
SurfaceBase* color{};
SurfaceBase* depth_stencil{};
SurfaceParams color_params{};
SurfaceParams depth_params{};
std::array<SurfaceInterval, 2> intervals{};
Common::Rectangle<s32> scissor_rect{};
Common::Rectangle<u32> draw_rect{};

File diff suppressed because it is too large Load Diff

View File

@ -46,21 +46,21 @@ class RasterizerCache : NonCopyable {
static constexpr u64 CITRA_PAGEBITS = 18;
using Runtime = typename T::Runtime;
using Surface = std::shared_ptr<typename T::Surface>;
using Surface = typename T::Surface;
using Sampler = typename T::Sampler;
using Framebuffer = typename T::Framebuffer;
/// Declare rasterizer interval types
using SurfaceMap = boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber,
using SurfaceMap = boost::icl::interval_map<PAddr, SurfaceId, boost::icl::partial_absorber,
std::less, boost::icl::inplace_plus,
boost::icl::inter_section, SurfaceInterval>;
using SurfaceRect_Tuple = std::tuple<Surface, Common::Rectangle<u32>>;
using SurfaceRect_Tuple = std::pair<SurfaceId, Common::Rectangle<u32>>;
using PageMap = boost::icl::interval_map<u32, int>;
struct RenderTargets {
Surface color_surface;
Surface depth_surface;
SurfaceId color_surface_id;
SurfaceId depth_surface_id;
};
public:
@ -77,17 +77,19 @@ public:
/// Perform hardware accelerated memory fill according to the provided configuration
bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config);
/// Returns a reference to the surface object assigned to surface_id
Surface& GetSurface(SurfaceId surface_id);
/// Returns a reference to the sampler object matching the provided configuration
Sampler& GetSampler(const Pica::TexturingRegs::TextureConfig& config);
Sampler& GetSampler(SamplerId sampler_id);
/// Copy one surface's region to another
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
SurfaceInterval copy_interval);
void CopySurface(Surface& src_surface, Surface& dst_surface, SurfaceInterval copy_interval);
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
SurfaceId GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
/// 3DS memory to OpenGL and caches it (if not already cached)
@ -95,11 +97,11 @@ public:
bool load_if_create);
/// Get a surface based on the texture configuration
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
Surface GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0);
Surface& GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
Surface& GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0);
/// Get a texture cube based on the texture configuration
const Surface& GetTextureCube(const TextureCubeConfig& config);
Surface& GetTextureCube(const TextureCubeConfig& config);
/// Get the color and depth surfaces based on the framebuffer configuration
Framebuffer GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
@ -111,10 +113,10 @@ public:
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
/// Write any cached resources overlapping the region back to memory (if dirty)
void FlushRegion(PAddr addr, u32 size, Surface flush_surface = nullptr);
void FlushRegion(PAddr addr, u32 size, SurfaceId flush_surface_id = {});
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
void InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner);
void InvalidateRegion(PAddr addr, u32 size, SurfaceId region_owner_id = {});
/// Flush all cached resources tracked by this cache manager
void FlushAll();
@ -145,47 +147,47 @@ private:
/// Get the best surface match (and its match type) for the given flags
template <MatchFlags find_flags>
Surface FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type,
std::optional<SurfaceInterval> validate_interval = std::nullopt);
SurfaceId FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type,
std::optional<SurfaceInterval> validate_interval = std::nullopt);
/// Duplicates src_surface contents to dest_surface
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
void DuplicateSurface(SurfaceId src_id, SurfaceId dst_id);
/// Update surface's texture for given region when necessary
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
void ValidateSurface(SurfaceId surface_id, PAddr addr, u32 size);
/// Copies pixel data in interval from the guest VRAM to the host GPU surface
void UploadSurface(const Surface& surface, SurfaceInterval interval);
void UploadSurface(Surface& surface, SurfaceInterval interval);
/// Uploads a custom texture associated with upload_data to the target surface
bool UploadCustomSurface(const Surface& surface, const SurfaceParams& load_info,
bool UploadCustomSurface(Surface& surface, const SurfaceParams& load_info,
std::span<u8> upload_data);
/// Copies pixel data in interval from the host GPU surface to the guest VRAM
void DownloadSurface(const Surface& surface, SurfaceInterval interval);
void DownloadSurface(Surface& surface, SurfaceInterval interval);
/// Downloads a fill surface to guest VRAM
void DownloadFillSurface(const Surface& surface, SurfaceInterval interval);
void DownloadFillSurface(Surface& surface, SurfaceInterval interval);
/// Returns false if there is a surface in the cache at the interval with the same bit-width,
bool NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams params,
bool NoUnimplementedReinterpretations(Surface& surface, SurfaceParams params,
SurfaceInterval interval);
/// Return true if a surface with an invalid pixel format exists at the interval
bool IntervalHasInvalidPixelFormat(SurfaceParams params, SurfaceInterval interval);
bool IntervalHasInvalidPixelFormat(const SurfaceParams params, SurfaceInterval interval);
/// Attempt to find a reinterpretable surface in the cache and use it to copy for validation
bool ValidateByReinterpretation(const Surface& surface, SurfaceParams params,
bool ValidateByReinterpretation(Surface& surface, SurfaceParams params,
SurfaceInterval interval);
/// Create a new surface
Surface CreateSurface(SurfaceParams& params);
SurfaceId CreateSurface(const SurfaceParams& params);
/// Register surface into the cache
void RegisterSurface(const Surface& surface);
void RegisterSurface(SurfaceId surface_id);
/// Remove surface from the cache
void UnregisterSurface(const Surface& surface);
void UnregisterSurface(SurfaceId surface_id);
/// Unregisters all surfaces from the cache
void UnregisterAll();
@ -199,16 +201,17 @@ private:
CustomTexManager& custom_tex_manager;
PageMap cached_pages;
SurfaceMap dirty_regions;
std::vector<Surface> remove_surfaces;
std::vector<SurfaceId> remove_surfaces;
u16 resolution_scale_factor;
std::unordered_map<TextureCubeConfig, Surface> texture_cube_cache;
std::unordered_map<TextureCubeConfig, SurfaceId> texture_cube_cache;
// The internal surface cache is based on buckets of 256KB.
// This fits better for the purpose of this cache as textures are normaly
// large in size.
std::unordered_map<u64, std::vector<Surface>, Common::IdentityHash<u64>> page_table;
std::unordered_map<u64, std::vector<SurfaceId>, Common::IdentityHash<u64>> page_table;
std::unordered_map<SamplerParams, SamplerId> samplers;
SlotVector<Surface> slot_surfaces;
SlotVector<Sampler> slot_samplers;
RenderTargets render_targets;

View File

@ -20,8 +20,7 @@ public:
/// Returns true when this surface can be used to fill the fill_interval of dest_surface
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
/// Returns true when copy_interval of dest_surface can be validated by copying from this
/// surface
/// Returns true when surface can validate copy_interval of dest_surface
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
/// Returns the region of the biggest valid rectange within interval

View File

@ -4,6 +4,7 @@
#pragma once
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/rasterizer_cache/utils.h"
namespace VideoCore {

View File

@ -10,46 +10,10 @@
namespace VideoCore {
ClearValue MakeClearValue(SurfaceType type, PixelFormat format, const u8* fill_data) {
ClearValue result{};
switch (type) {
case SurfaceType::Color:
case SurfaceType::Texture:
case SurfaceType::Fill: {
Pica::Texture::TextureInfo tex_info{};
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(format);
const auto color = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info);
result.color = color / 255.f;
break;
}
case SurfaceType::Depth: {
u32 depth_uint = 0;
if (format == PixelFormat::D16) {
std::memcpy(&depth_uint, fill_data, 2);
result.depth = depth_uint / 65535.0f; // 2^16 - 1
} else if (format == PixelFormat::D24) {
std::memcpy(&depth_uint, fill_data, 3);
result.depth = depth_uint / 16777215.0f; // 2^24 - 1
}
break;
}
case SurfaceType::DepthStencil: {
u32 clear_value_uint;
std::memcpy(&clear_value_uint, fill_data, sizeof(u32));
result.depth = (clear_value_uint & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
result.stencil = (clear_value_uint >> 24);
break;
}
default:
UNREACHABLE_MSG("Invalid surface type!");
}
return result;
}
void EncodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
std::span<u8> source, std::span<u8> dest, bool convert) {
const u32 func_index = static_cast<u32>(surface_info.pixel_format);
const PixelFormat format = surface_info.pixel_format;
const u32 func_index = static_cast<u32>(format);
if (surface_info.is_tiled) {
const MortonFunc SwizzleImpl =
@ -87,7 +51,8 @@ u32 MipLevels(u32 width, u32 height, u32 max_level) {
void DecodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
std::span<u8> source, std::span<u8> dest, bool convert) {
const u32 func_index = static_cast<u32>(surface_info.pixel_format);
const PixelFormat format = surface_info.pixel_format;
const u32 func_index = static_cast<u32>(format);
if (surface_info.is_tiled) {
const MortonFunc UnswizzleImpl =

View File

@ -9,15 +9,18 @@
#include "common/hash.h"
#include "common/math_util.h"
#include "common/vector_math.h"
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/rasterizer_cache/slot_vector.h"
#include "video_core/regs_texturing.h"
namespace VideoCore {
using Rect2D = Common::Rectangle<u32>;
using SurfaceId = SlotId;
using SamplerId = SlotId;
/// Fake surface ID for null surfaces
constexpr SurfaceId NULL_SURFACE_ID{0};
/// Fake sampler ID for null samplers
constexpr SamplerId NULL_SAMPLER_ID{0};
@ -103,8 +106,6 @@ struct TextureCubeConfig {
class SurfaceParams;
[[nodiscard]] ClearValue MakeClearValue(SurfaceType type, PixelFormat format, const u8* fill_data);
u32 MipLevels(u32 width, u32 height, u32 max_level);
/**

View File

@ -4,6 +4,7 @@
#pragma once
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/rasterizer_cache/utils.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"

View File

@ -52,13 +52,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory,
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
state.clip_distance[0] = true;
// Create a 1x1 clear texture to use in the NULL case,
// instead of OpenGL's default of solid black
default_texture.Create();
// For some reason alpha 0 wraps around to 1.0, so use 1/255 instead
u8 framebuffer_data[4] = {0, 0, 0, 1};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
// Generate VAO
sw_vao.Create();
hw_vao.Create();
@ -212,7 +205,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
u32 vertex_num = vs_input_index_max - vs_input_index_min + 1;
u32 data_size = loader.byte_count * vertex_num;
res_cache.FlushRegion(data_addr, data_size, nullptr);
res_cache.FlushRegion(data_addr, data_size);
std::memcpy(array_ptr, memory.GetPhysicalPointer(data_addr), data_size);
array_ptr += data_size;
@ -409,95 +402,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
uniform_block_data.dirty = true;
}
const auto BindCubeFace = [&](GLuint& target, Pica::TexturingRegs::CubeFace face,
Pica::Texture::TextureInfo& info) {
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
auto surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
target = surface->Handle();
} else {
target = 0;
}
};
// Sync and bind the texture surfaces
const auto pica_textures = regs.texturing.GetTextures();
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
const auto& texture = pica_textures[texture_index];
if (texture.enabled) {
Sampler& sampler = res_cache.GetSampler(texture.config);
if (texture_index == 0) {
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
switch (texture.config.type.Value()) {
case TextureType::Shadow2D: {
const auto surface = res_cache.GetTextureSurface(texture);
if (surface) {
state.image_shadow_texture_px = surface->Handle();
} else {
state.image_shadow_texture_px = 0;
}
continue;
}
case TextureType::ShadowCube: {
using CubeFace = Pica::TexturingRegs::CubeFace;
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config,
texture.format);
BindCubeFace(state.image_shadow_texture_px, CubeFace::PositiveX, info);
BindCubeFace(state.image_shadow_texture_nx, CubeFace::NegativeX, info);
BindCubeFace(state.image_shadow_texture_py, CubeFace::PositiveY, info);
BindCubeFace(state.image_shadow_texture_ny, CubeFace::NegativeY, info);
BindCubeFace(state.image_shadow_texture_pz, CubeFace::PositiveZ, info);
BindCubeFace(state.image_shadow_texture_nz, CubeFace::NegativeZ, info);
continue;
}
case TextureType::TextureCube: {
using CubeFace = Pica::TexturingRegs::CubeFace;
const VideoCore::TextureCubeConfig config = {
.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
.width = texture.config.width,
.levels = texture.config.lod.max_level + 1,
.format = texture.format};
state.texture_cube_unit.texture_cube =
res_cache.GetTextureCube(config)->Handle();
state.texture_cube_unit.sampler = sampler.Handle();
state.texture_units[texture_index].texture_2d = 0;
continue; // Texture unit 0 setup finished. Continue to next unit
}
default:
break;
}
state.texture_cube_unit.texture_cube = 0;
}
state.texture_units[texture_index].sampler = sampler.Handle();
auto surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) {
state.texture_units[texture_index].texture_2d = surface->Handle();
} else {
// Can occur when texture addr is null or its memory is unmapped/invalid
// HACK: In this case, the correct behaviour for the PICA is to use the last
// rendered colour. But because this would be impractical to implement, the
// next best alternative is to use a clear texture, essentially skipping
// the geometry in question.
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
// on the male character's face, which in the OpenGL default appear black.
state.texture_units[texture_index].texture_2d = default_texture.handle;
}
} else {
state.texture_units[texture_index].texture_2d = 0;
}
}
SyncTextureUnits(framebuffer);
// Sync and bind the shader
if (shader_dirty) {
@ -562,6 +468,114 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
return succeeded;
}
void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) {
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
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];
// If the texture unit is disabled unbind the corresponding gl unit
if (!texture.enabled) {
state.texture_units[texture_index].texture_2d = 0;
continue;
}
// Handle special tex0 configurations
if (texture_index == 0) {
switch (texture.config.type.Value()) {
case TextureType::Shadow2D: {
Surface& surface = res_cache.GetTextureSurface(texture);
state.image_shadow_texture_px = surface.Handle();
continue;
}
case TextureType::ShadowCube: {
BindShadowCube(texture);
continue;
}
case TextureType::TextureCube: {
BindTextureCube(texture);
continue;
}
default:
break;
}
}
// Bind texture unit sampler
Sampler& sampler = res_cache.GetSampler(texture.config);
state.texture_units[texture_index].sampler = sampler.Handle();
// Bind the texture provided by the rasterizer cache
Surface& surface = res_cache.GetTextureSurface(texture);
if (!IsFeedbackLoop(texture_index, framebuffer, surface)) {
state.texture_units[texture_index].texture_2d = surface.Handle();
}
}
}
void RasterizerOpenGL::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
using CubeFace = Pica::TexturingRegs::CubeFace;
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
constexpr std::array faces = {
CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY,
CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ,
};
for (CubeFace face : faces) {
const u32 binding = static_cast<u32>(face);
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
Surface& surface = res_cache.GetTextureSurface(info);
state.image_shadow_texture[binding] = surface.Handle();
}
}
void RasterizerOpenGL::BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
using CubeFace = Pica::TexturingRegs::CubeFace;
const VideoCore::TextureCubeConfig config = {
.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
.width = texture.config.width,
.levels = texture.config.lod.max_level + 1,
.format = texture.format,
};
Surface& surface = res_cache.GetTextureCube(config);
Sampler& sampler = res_cache.GetSampler(texture.config);
state.texture_cube_unit.texture_cube = surface.Handle();
state.texture_cube_unit.sampler = sampler.Handle();
state.texture_units[0].texture_2d = 0;
}
bool RasterizerOpenGL::IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer,
Surface& surface) {
const GLuint color_attachment = framebuffer.Attachment(SurfaceType::Color);
const bool is_feedback_loop = color_attachment == surface.Handle();
if (!is_feedback_loop) {
return false;
}
// Make a temporary copy of the framebuffer to sample from
Surface temp_surface{runtime, framebuffer.ColorParams()};
const VideoCore::TextureCopy copy = {
.src_level = 0,
.dst_level = 0,
.src_layer = 0,
.dst_layer = 0,
.src_offset = {0, 0},
.dst_offset = {0, 0},
.extent = {temp_surface.GetScaledWidth(), temp_surface.GetScaledHeight()},
};
runtime.CopyTextures(surface, temp_surface, copy);
state.texture_units[texture_index].texture_2d = temp_surface.Handle();
return true;
}
void RasterizerOpenGL::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
switch (id) {
// Clipping plane
@ -641,13 +655,13 @@ void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.InvalidateRegion(addr, size, nullptr);
res_cache.InvalidateRegion(addr, size);
}
void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushRegion(addr, size);
res_cache.InvalidateRegion(addr, size, nullptr);
res_cache.InvalidateRegion(addr, size);
}
void RasterizerOpenGL::ClearAll(bool flush) {
@ -684,21 +698,22 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format);
src_params.UpdateParams();
const auto [src_surface, src_rect] =
const auto [src_surface_id, src_rect] =
res_cache.GetSurfaceSubRect(src_params, VideoCore::ScaleMatch::Ignore, true);
if (src_surface == nullptr) {
if (!src_surface_id) {
return false;
}
const u32 scaled_width = src_surface->GetScaledWidth();
const u32 scaled_height = src_surface->GetScaledHeight();
Surface& src_surface = res_cache.GetSurface(src_surface_id);
const u32 scaled_width = src_surface.GetScaledWidth();
const u32 scaled_height = src_surface.GetScaledHeight();
screen_info.display_texcoords = Common::Rectangle<float>(
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
(float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
screen_info.display_texture = src_surface->Handle();
screen_info.display_texture = src_surface.Handle();
return true;
}

View File

@ -94,6 +94,18 @@ private:
void SyncAndUploadLUTs();
void SyncAndUploadLUTsLF();
/// Syncs all enabled PICA texture units
void SyncTextureUnits(const Framebuffer& framebuffer);
/// Binds the PICA shadow cube required for shadow mapping
void BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture);
/// Binds a texture cube to texture unit 0
void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture);
/// Makes a temporary copy of the framebuffer if a feedback loop is detected
bool IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, Surface& surface);
/// Upload the uniform blocks to the uniform buffer object
void UploadUniforms(bool accelerate_draw);
@ -124,7 +136,6 @@ private:
OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
std::array<bool, 16> hw_vao_enabled_attributes{};
OGLTexture default_texture;
StreamBuffer vertex_buffer;
StreamBuffer uniform_buffer;
StreamBuffer index_buffer;

View File

@ -116,12 +116,17 @@ public:
// GL_IMAGE_BINDING_NAME
GLuint image_shadow_buffer;
GLuint image_shadow_texture_px;
GLuint image_shadow_texture_nx;
GLuint image_shadow_texture_py;
GLuint image_shadow_texture_ny;
GLuint image_shadow_texture_pz;
GLuint image_shadow_texture_nz;
union {
std::array<GLuint, 6> image_shadow_texture;
struct {
GLuint image_shadow_texture_px;
GLuint image_shadow_texture_nx;
GLuint image_shadow_texture_py;
GLuint image_shadow_texture_ny;
GLuint image_shadow_texture_pz;
GLuint image_shadow_texture_nz;
};
};
struct {
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING

View File

@ -332,16 +332,16 @@ void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarge
}
}
Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
: VideoCore::SurfaceBase{params}, runtime{runtime}, driver{runtime.GetDriver()} {
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params)
: VideoCore::SurfaceBase{params}, runtime{&runtime_}, driver{&runtime_.GetDriver()} {
if (pixel_format != VideoCore::PixelFormat::Invalid) {
const auto& tuple = runtime.GetFormatTuple(pixel_format);
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, tuple, texture_type);
const auto& tuple = runtime->GetFormatTuple(pixel_format);
alloc = runtime->Allocate(GetScaledWidth(), GetScaledHeight(), levels, tuple, texture_type);
}
}
Surface::~Surface() {
if (pixel_format == VideoCore::PixelFormat::Invalid) {
if (pixel_format == VideoCore::PixelFormat::Invalid || !Handle()) {
return;
}
@ -352,7 +352,7 @@ Surface::~Surface() {
.height = alloc.height,
.levels = alloc.levels,
};
runtime.Recycle(tag, std::move(alloc));
runtime->Recycle(tag, std::move(alloc));
}
void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) {
@ -398,10 +398,10 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
const VideoCore::Rect2D rect = download.texture_rect;
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(rect.GetWidth()));
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
Handle());
runtime->BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
Handle());
const auto& tuple = runtime.GetFormatTuple(pixel_format);
const auto& tuple = runtime->GetFormatTuple(pixel_format);
glReadPixels(rect.left, rect.bottom, rect.GetWidth(), rect.GetHeight(), tuple.format,
tuple.type, staging.mapped.data());
@ -413,11 +413,11 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
}
bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) {
if (!driver.IsCustomFormatSupported(format)) {
if (!driver->IsCustomFormatSupported(format)) {
return false;
}
const auto& tuple = runtime.GetFormatTuple(format);
const auto& tuple = runtime->GetFormatTuple(format);
if (alloc.Matches(width, height, levels, tuple)) {
return true;
}
@ -429,11 +429,11 @@ bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) {
.height = alloc.height,
.levels = alloc.levels,
};
runtime.Recycle(tag, std::move(alloc));
runtime->Recycle(tag, std::move(alloc));
is_custom = true;
custom_format = format;
alloc = runtime.Allocate(width, height, levels, tuple, texture_type);
alloc = runtime->Allocate(width, height, levels, tuple, texture_type);
LOG_DEBUG(Render_OpenGL, "Swapped {}x{} {} surface at address {:#x} to {}x{} {}",
GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format),
@ -453,7 +453,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta
unscaled_params.stride = rect_width;
unscaled_params.height = rect_height;
unscaled_params.res_scale = 1;
Surface unscaled_surface{unscaled_params, runtime};
Surface unscaled_surface{*runtime, unscaled_params};
const VideoCore::BufferTextureCopy unscaled_upload = {
.buffer_offset = upload.buffer_offset,
@ -462,7 +462,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta
};
unscaled_surface.Upload(unscaled_upload, staging);
const auto& filterer = runtime.GetFilterer();
const auto& filterer = runtime->GetFilterer();
if (!filterer.Filter(unscaled_surface.alloc.texture, unscaled_rect, alloc.texture, scaled_rect,
type)) {
const VideoCore::TextureBlit blit = {
@ -471,7 +471,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta
.src_rect = unscaled_rect,
.dst_rect = scaled_rect,
};
runtime.BlitTextures(unscaled_surface, *this, blit);
runtime->BlitTextures(unscaled_surface, *this, blit);
}
}
@ -488,7 +488,7 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
unscaled_params.stride = rect_width;
unscaled_params.height = rect_height;
unscaled_params.res_scale = 1;
Surface unscaled_surface{unscaled_params, runtime};
Surface unscaled_surface{*runtime, unscaled_params};
// Blit the scaled rectangle to the unscaled texture
const VideoCore::TextureBlit blit = {
@ -499,15 +499,15 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
.src_rect = scaled_rect,
.dst_rect = unscaled_rect,
};
runtime.BlitTextures(*this, unscaled_surface, blit);
runtime->BlitTextures(*this, unscaled_surface, blit);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, unscaled_surface.Handle());
const auto& tuple = runtime.GetFormatTuple(pixel_format);
if (driver.IsOpenGLES()) {
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
unscaled_surface.Handle());
const auto& tuple = runtime->GetFormatTuple(pixel_format);
if (driver->IsOpenGLES()) {
runtime->BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
unscaled_surface.Handle());
glReadPixels(0, 0, rect_width, rect_height, tuple.format, tuple.type,
staging.mapped.data());
} else {

View File

@ -153,9 +153,15 @@ private:
class Surface : public VideoCore::SurfaceBase {
public:
explicit Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime);
explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params);
~Surface();
Surface(const Surface&) = delete;
Surface& operator=(const Surface&) = delete;
Surface(Surface&& o) noexcept = default;
Surface& operator=(Surface&& o) noexcept = default;
/// Returns the surface image handle
GLuint Handle() const noexcept {
return alloc.texture.handle;
@ -186,9 +192,9 @@ private:
const VideoCore::StagingData& staging);
private:
TextureRuntime& runtime;
const Driver& driver;
Allocation alloc;
TextureRuntime* runtime;
const Driver* driver;
Allocation alloc{};
};
class Framebuffer : public VideoCore::FramebufferBase {

View File

@ -13,7 +13,6 @@
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#define VULKAN_HPP_NO_CONSTRUCTORS
#define VULKAN_HPP_NO_STRUCT_SETTERS
#define VULKAN_HPP_NO_SMART_HANDLE
#include <vulkan/vulkan.hpp>
#define VMA_STATIC_VULKAN_FUNCTIONS 0

View File

@ -4,6 +4,7 @@
#pragma once
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/rasterizer_cache/utils.h"
#include "video_core/renderer_vulkan/vk_common.h"

View File

@ -37,20 +37,6 @@ constexpr vk::BufferUsageFlags BUFFER_USAGE = vk::BufferUsageFlagBits::eVertexBu
constexpr vk::BufferUsageFlags TEX_BUFFER_USAGE = vk::BufferUsageFlagBits::eUniformTexelBuffer;
constexpr VideoCore::SurfaceParams NULL_PARAMS = {
.width = 1,
.height = 1,
.stride = 1,
.texture_type = VideoCore::TextureType::Texture2D,
.pixel_format = VideoCore::PixelFormat::RGBA8,
.type = VideoCore::SurfaceType::Color,
};
constexpr vk::ImageUsageFlags NULL_USAGE = vk::ImageUsageFlagBits::eSampled |
vk::ImageUsageFlagBits::eTransferSrc |
vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags NULL_STORAGE_USAGE = NULL_USAGE | vk::ImageUsageFlagBits::eStorage;
struct DrawParams {
u32 vertex_count;
s32 vertex_offset;
@ -78,10 +64,6 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
custom_tex_manager,
runtime},
pipeline_cache{instance, scheduler, renderpass_cache, desc_manager},
null_surface{NULL_PARAMS, vk::Format::eR8G8B8A8Unorm, NULL_USAGE,
vk::ImageAspectFlagBits::eColor, runtime},
null_storage_surface{NULL_PARAMS, vk::Format::eR32Uint, NULL_STORAGE_USAGE,
vk::ImageAspectFlagBits::eColor, runtime},
stream_buffer{instance, scheduler, BUFFER_USAGE, STREAM_BUFFER_SIZE},
texture_buffer{instance, scheduler, TEX_BUFFER_USAGE, TextureBufferSize(instance)},
texture_lf_buffer{instance, scheduler, TEX_BUFFER_USAGE, TextureBufferSize(instance)},
@ -127,18 +109,17 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
pipeline_cache.BindTexelBuffer(3, texture_rg_view);
pipeline_cache.BindTexelBuffer(4, texture_rgba_view);
const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID);
Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID);
for (u32 i = 0; i < 4; i++) {
pipeline_cache.BindTexture(i, null_surface.ImageView(), null_sampler.Handle());
}
for (u32 i = 0; i < 7; i++) {
pipeline_cache.BindStorageImage(i, null_storage_surface.ImageView());
pipeline_cache.BindStorageImage(i, null_surface.StorageView());
}
// Explicitly call the derived version to avoid warnings about calling virtual
// methods in the constructor
RasterizerVulkan::SyncEntireState();
SyncEntireState();
}
RasterizerVulkan::~RasterizerVulkan() {
@ -566,111 +547,111 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
}
void RasterizerVulkan::SyncTextureUnits(const Framebuffer& framebuffer) {
const auto pica_textures = regs.texturing.GetTextures();
const vk::ImageView color_view = framebuffer.ImageView(SurfaceType::Color);
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
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];
if (texture.enabled) {
Sampler& sampler = res_cache.GetSampler(texture.config);
if (texture_index == 0) {
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
switch (texture.config.type.Value()) {
case TextureType::Shadow2D: {
auto surface = res_cache.GetTextureSurface(texture);
if (surface) {
pipeline_cache.BindStorageImage(0, surface->StorageView());
} else {
pipeline_cache.BindStorageImage(0, null_storage_surface.ImageView());
}
continue;
}
case TextureType::ShadowCube: {
using CubeFace = Pica::TexturingRegs::CubeFace;
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config,
texture.format);
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: {
using CubeFace = Pica::TexturingRegs::CubeFace;
const VideoCore::TextureCubeConfig config = {
.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
.width = texture.config.width,
.levels = texture.config.lod.max_level + 1,
.format = texture.format};
auto surface = res_cache.GetTextureCube(config);
if (surface) {
pipeline_cache.BindTexture(3, surface->ImageView(), sampler.Handle());
} else {
pipeline_cache.BindTexture(3, null_surface.ImageView(), sampler.Handle());
}
continue; // Texture unit 0 setup finished. Continue to next unit
}
default:
break;
}
}
auto surface = res_cache.GetTextureSurface(texture);
if (surface) {
if (color_view == surface->ImageView()) {
Surface temp{*framebuffer.Color(), runtime};
const VideoCore::TextureCopy copy = {
.src_level = 0,
.dst_level = 0,
.src_layer = 0,
.dst_layer = 0,
.src_offset = {0, 0},
.dst_offset = {0, 0},
.extent = {temp.GetScaledWidth(), temp.GetScaledHeight()},
};
runtime.CopyTextures(static_cast<Surface&>(*framebuffer.Color()), temp, copy);
pipeline_cache.BindTexture(texture_index, temp.ImageView(), sampler.Handle());
} else {
pipeline_cache.BindTexture(texture_index, surface->ImageView(),
sampler.Handle());
}
} else {
// Can occur when texture addr is null or its memory is unmapped/invalid
// HACK: In this case, the correct behaviour for the PICA is to use the last
// rendered colour. But because this would be impractical to implement, the
// next best alternative is to use a clear texture, essentially skipping
// the geometry in question.
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
// on the male character's face, which in the OpenGL default appear black.
pipeline_cache.BindTexture(texture_index, null_surface.ImageView(),
sampler.Handle());
}
} else {
// If the texture unit is disabled bind a null surface to it
if (!texture.enabled) {
const Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID);
pipeline_cache.BindTexture(texture_index, null_surface.ImageView(),
null_sampler.Handle());
continue;
}
// Handle special tex0 configurations
if (texture_index == 0) {
switch (texture.config.type.Value()) {
case TextureType::Shadow2D: {
Surface& surface = res_cache.GetTextureSurface(texture);
pipeline_cache.BindStorageImage(0, surface.StorageView());
continue;
}
case TextureType::ShadowCube: {
BindShadowCube(texture);
continue;
}
case TextureType::TextureCube: {
BindTextureCube(texture);
continue;
}
default:
break;
}
}
// Bind the texture provided by the rasterizer cache
Surface& surface = res_cache.GetTextureSurface(texture);
Sampler& sampler = res_cache.GetSampler(texture.config);
if (!IsFeedbackLoop(texture_index, framebuffer, surface, sampler)) {
pipeline_cache.BindTexture(texture_index, surface.ImageView(), sampler.Handle());
}
}
}
void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
using CubeFace = Pica::TexturingRegs::CubeFace;
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
constexpr std::array faces = {
CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY,
CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ,
};
for (CubeFace face : faces) {
const u32 binding = static_cast<u32>(face);
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
Surface& surface = res_cache.GetTextureSurface(info);
pipeline_cache.BindStorageImage(binding, surface.ImageView());
}
}
void RasterizerVulkan::BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
using CubeFace = Pica::TexturingRegs::CubeFace;
const VideoCore::TextureCubeConfig config = {
.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
.width = texture.config.width,
.levels = texture.config.lod.max_level + 1,
.format = texture.format,
};
Surface& surface = res_cache.GetTextureCube(config);
Sampler& sampler = res_cache.GetSampler(texture.config);
pipeline_cache.BindTexture(3, surface.ImageView(), sampler.Handle());
}
bool RasterizerVulkan::IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer,
Surface& surface, Sampler& sampler) {
const vk::ImageView color_view = framebuffer.ImageView(SurfaceType::Color);
const bool is_feedback_loop = color_view == surface.ImageView();
if (!is_feedback_loop) {
return false;
}
// Make a temporary copy of the framebuffer to sample from
Surface temp_surface{runtime, framebuffer.ColorParams()};
const VideoCore::TextureCopy copy = {
.src_level = 0,
.dst_level = 0,
.src_layer = 0,
.dst_layer = 0,
.src_offset = {0, 0},
.dst_offset = {0, 0},
.extent = {temp_surface.GetScaledWidth(), temp_surface.GetScaledHeight()},
};
runtime.CopyTextures(surface, temp_surface, copy);
pipeline_cache.BindTexture(texture_index, temp_surface.ImageView(), sampler.Handle());
return true;
}
void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
switch (id) {
// Clipping plane
@ -750,13 +731,13 @@ void RasterizerVulkan::FlushRegion(PAddr addr, u32 size) {
void RasterizerVulkan::InvalidateRegion(PAddr addr, u32 size) {
MICROPROFILE_SCOPE(Vulkan_CacheManagement);
res_cache.InvalidateRegion(addr, size, nullptr);
res_cache.InvalidateRegion(addr, size);
}
void RasterizerVulkan::FlushAndInvalidateRegion(PAddr addr, u32 size) {
MICROPROFILE_SCOPE(Vulkan_CacheManagement);
res_cache.FlushRegion(addr, size);
res_cache.InvalidateRegion(addr, size, nullptr);
res_cache.InvalidateRegion(addr, size);
}
void RasterizerVulkan::ClearAll(bool flush) {
@ -793,21 +774,22 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format);
src_params.UpdateParams();
const auto [src_surface, src_rect] =
const auto [src_surface_id, src_rect] =
res_cache.GetSurfaceSubRect(src_params, VideoCore::ScaleMatch::Ignore, true);
if (src_surface == nullptr) {
if (!src_surface_id) {
return false;
}
const u32 scaled_width = src_surface->GetScaledWidth();
const u32 scaled_height = src_surface->GetScaledHeight();
const Surface& src_surface = res_cache.GetSurface(src_surface_id);
const u32 scaled_width = src_surface.GetScaledWidth();
const u32 scaled_height = src_surface.GetScaledHeight();
screen_info.texcoords = Common::Rectangle<f32>(
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
(float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
screen_info.image_view = src_surface->ImageView();
screen_info.image_view = src_surface.ImageView();
return true;
}
@ -815,7 +797,10 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
void RasterizerVulkan::MakeSoftwareVertexLayout() {
constexpr std::array sizes = {4, 4, 2, 2, 2, 1, 4, 3};
software_layout = VertexLayout{.binding_count = 1, .attribute_count = 8};
software_layout = VertexLayout{
.binding_count = 1,
.attribute_count = 8,
};
for (u32 i = 0; i < software_layout.binding_count; i++) {
VertexBinding& binding = software_layout.bindings[i];

View File

@ -100,6 +100,16 @@ private:
/// Syncs all enabled PICA texture units
void SyncTextureUnits(const Framebuffer& framebuffer);
/// Binds the PICA shadow cube required for shadow mapping
void BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture);
/// Binds a texture cube to texture unit 0
void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture);
/// Makes a temporary copy of the framebuffer if a feedback loop is detected
bool IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, Surface& surface,
Sampler& sampler);
/// Upload the uniform blocks to the uniform buffer object
void UploadUniforms(bool accelerate_draw);
@ -140,8 +150,6 @@ private:
std::array<u32, 16> binding_offsets{};
std::array<bool, 16> enable_attributes{};
std::array<vk::Buffer, 16> vertex_buffers;
Surface null_surface;
Surface null_storage_surface;
PipelineInfo pipeline_info;
StreamBuffer stream_buffer; ///< Vertex+Index+Uniform buffer

View File

@ -159,14 +159,6 @@ void TextureRuntime::Clear() {
renderpass_cache.ClearFramebuffers();
for (const auto& [key, alloc] : texture_recycler) {
vmaDestroyImage(allocator, alloc.image, alloc.allocation);
device.destroyImageView(alloc.image_view);
if (alloc.depth_view) {
device.destroyImageView(alloc.depth_view);
device.destroyImageView(alloc.stencil_view);
}
if (alloc.storage_view) {
device.destroyImageView(alloc.storage_view);
}
}
texture_recycler.clear();
@ -269,7 +261,7 @@ Allocation TextureRuntime::Allocate(u32 width, u32 height, u32 levels, bool is_m
};
vk::Device device = instance.GetDevice();
const vk::ImageView image_view = device.createImageView(view_info);
vk::UniqueImageView image_view = device.createImageViewUnique(view_info);
renderpass_cache.ExitRenderpass();
scheduler.Record([image, aspect](vk::CommandBuffer cmdbuf) {
@ -297,7 +289,7 @@ Allocation TextureRuntime::Allocate(u32 width, u32 height, u32 levels, bool is_m
return Allocation{
.image = image,
.image_view = image_view,
.image_view = std::move(image_view),
.allocation = allocation,
.aspect = aspect,
.format = format,
@ -778,28 +770,28 @@ bool TextureRuntime::NeedsConvertion(VideoCore::PixelFormat format) const {
traits.aspect != (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil);
}
Surface::Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime)
: VideoCore::SurfaceBase{params}, runtime{runtime}, instance{runtime.GetInstance()},
scheduler{runtime.GetScheduler()} {
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params)
: VideoCore::SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()},
scheduler{&runtime_.GetScheduler()} {
if (pixel_format != VideoCore::PixelFormat::Invalid) {
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, params.pixel_format,
texture_type);
alloc = runtime->Allocate(GetScaledWidth(), GetScaledHeight(), levels, params.pixel_format,
texture_type);
}
}
Surface::Surface(const VideoCore::SurfaceParams& params, vk::Format format,
vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect, TextureRuntime& runtime)
: VideoCore::SurfaceBase{params}, runtime{runtime}, instance{runtime.GetInstance()},
scheduler{runtime.GetScheduler()} {
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params,
vk::Format format, vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect)
: VideoCore::SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()},
scheduler{&runtime_.GetScheduler()} {
if (format != vk::Format::eUndefined) {
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, false, texture_type,
format, usage, aspect);
alloc = runtime->Allocate(GetScaledWidth(), GetScaledHeight(), levels, false, texture_type,
format, usage, aspect);
}
}
Surface::~Surface() {
if (pixel_format == VideoCore::PixelFormat::Invalid) {
if (pixel_format == VideoCore::PixelFormat::Invalid || !alloc.image_view) {
return;
}
@ -811,11 +803,11 @@ Surface::~Surface() {
.levels = alloc.levels,
.is_mutable = alloc.is_mutable,
};
runtime.Recycle(tag, std::move(alloc));
runtime->Recycle(tag, std::move(alloc));
}
void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) {
runtime.renderpass_cache.ExitRenderpass();
runtime->renderpass_cache.ExitRenderpass();
const bool is_scaled = res_scale != 1;
if (is_scaled) {
@ -828,8 +820,8 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa
.src_image = alloc.image,
};
scheduler.Record([buffer = runtime.upload_buffer.Handle(), format = alloc.format, params,
staging, upload](vk::CommandBuffer cmdbuf) {
scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = alloc.format, params,
staging, upload](vk::CommandBuffer cmdbuf) {
u32 num_copies = 1;
std::array<vk::BufferImageCopy, 2> buffer_image_copies;
@ -901,12 +893,12 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa
vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier);
});
runtime.upload_buffer.Commit(staging.size);
runtime->upload_buffer.Commit(staging.size);
}
}
void Surface::Download(const VideoCore::BufferTextureCopy& download, const StagingData& staging) {
runtime.renderpass_cache.ExitRenderpass();
runtime->renderpass_cache.ExitRenderpass();
// For depth stencil downloads always use the compute shader fallback
// to avoid having the interleave the data later. These should(?) be
@ -926,8 +918,8 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
.src_image = alloc.image,
};
scheduler.Record([buffer = runtime.download_buffer.Handle(), params, staging,
download](vk::CommandBuffer cmdbuf) {
scheduler->Record([buffer = runtime->download_buffer.Handle(), params, staging,
download](vk::CommandBuffer cmdbuf) {
const VideoCore::Rect2D rect = download.texture_rect;
const vk::BufferImageCopy buffer_image_copy = {
.bufferOffset = staging.buffer_offset + download.buffer_offset,
@ -990,12 +982,12 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
vk::DependencyFlagBits::eByRegion, memory_write_barrier, {},
image_write_barrier);
});
runtime.download_buffer.Commit(staging.size);
runtime->download_buffer.Commit(staging.size);
}
}
bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) {
const FormatTraits& traits = instance.GetTraits(format);
const FormatTraits& traits = instance->GetTraits(format);
if (!traits.transfer_support) {
return false;
}
@ -1013,12 +1005,12 @@ bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) {
.levels = levels,
.is_mutable = alloc.is_mutable,
};
runtime.Recycle(tag, std::move(alloc));
runtime->Recycle(tag, std::move(alloc));
is_custom = true;
custom_format = format;
alloc = runtime.Allocate(width, height, levels, false, texture_type, custom_vk_format,
traits.usage, traits.aspect);
alloc = runtime->Allocate(width, height, levels, false, texture_type, custom_vk_format,
traits.usage, traits.aspect);
LOG_DEBUG(Render_Vulkan, "Swapped {}x{} {} surface at address {:#x} to {}x{} {}",
GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format),
@ -1065,15 +1057,15 @@ vk::PipelineStageFlags Surface::PipelineStageFlags() const noexcept {
}
vk::ImageView Surface::DepthView() noexcept {
vk::ImageView& depth_view = alloc.depth_view;
vk::UniqueImageView& depth_view = alloc.depth_view;
if (depth_view) {
return depth_view;
return depth_view.get();
}
const vk::ImageViewCreateInfo view_info = {
.image = alloc.image,
.viewType = vk::ImageViewType::e2D,
.format = instance.GetTraits(pixel_format).native,
.format = instance->GetTraits(pixel_format).native,
.subresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
@ -1083,20 +1075,20 @@ vk::ImageView Surface::DepthView() noexcept {
},
};
depth_view = instance.GetDevice().createImageView(view_info);
return depth_view;
depth_view = instance->GetDevice().createImageViewUnique(view_info);
return depth_view.get();
}
vk::ImageView Surface::StencilView() noexcept {
vk::ImageView& stencil_view = alloc.stencil_view;
vk::UniqueImageView& stencil_view = alloc.stencil_view;
if (stencil_view) {
return stencil_view;
return stencil_view.get();
}
const vk::ImageViewCreateInfo view_info = {
.image = alloc.image,
.viewType = vk::ImageViewType::e2D,
.format = instance.GetTraits(pixel_format).native,
.format = instance->GetTraits(pixel_format).native,
.subresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eStencil,
.baseMipLevel = 0,
@ -1106,14 +1098,14 @@ vk::ImageView Surface::StencilView() noexcept {
},
};
stencil_view = instance.GetDevice().createImageView(view_info);
return stencil_view;
stencil_view = instance->GetDevice().createImageViewUnique(view_info);
return stencil_view.get();
}
vk::ImageView Surface::StorageView() noexcept {
vk::ImageView& storage_view = alloc.storage_view;
vk::UniqueImageView& storage_view = alloc.storage_view;
if (storage_view) {
return storage_view;
return storage_view.get();
}
ASSERT_MSG(pixel_format == VideoCore::PixelFormat::RGBA8,
@ -1132,8 +1124,8 @@ vk::ImageView Surface::StorageView() noexcept {
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
storage_view = instance.GetDevice().createImageView(storage_view_info);
return storage_view;
storage_view = instance->GetDevice().createImageViewUnique(storage_view_info);
return storage_view.get();
}
void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) {
@ -1147,14 +1139,13 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta
unscaled_params.stride = rect_width;
unscaled_params.height = rect_height;
unscaled_params.res_scale = 1;
Surface unscaled_surface{unscaled_params, runtime};
Surface unscaled_surface{*runtime, unscaled_params};
const VideoCore::BufferTextureCopy unscaled_upload = {
.buffer_offset = upload.buffer_offset,
.buffer_size = upload.buffer_size,
.texture_rect = unscaled_rect,
};
unscaled_surface.Upload(unscaled_upload, staging);
const VideoCore::TextureBlit blit = {
@ -1165,8 +1156,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta
.src_rect = unscaled_rect,
.dst_rect = scaled_rect,
};
runtime.BlitTextures(unscaled_surface, *this, blit);
runtime->BlitTextures(unscaled_surface, *this, blit);
}
void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
@ -1182,7 +1172,7 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
unscaled_params.stride = rect_width;
unscaled_params.height = rect_height;
unscaled_params.res_scale = 1;
Surface unscaled_surface{unscaled_params, runtime};
Surface unscaled_surface{*runtime, unscaled_params};
const VideoCore::TextureBlit blit = {
.src_level = download.texture_level,
@ -1192,9 +1182,7 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
.src_rect = scaled_rect,
.dst_rect = unscaled_rect,
};
// Blit the scaled rectangle to the unscaled texture
runtime.BlitTextures(*this, unscaled_surface, blit);
runtime->BlitTextures(*this, unscaled_surface, blit);
const VideoCore::BufferTextureCopy unscaled_download = {
.buffer_offset = download.buffer_offset,
@ -1202,7 +1190,6 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
.texture_rect = unscaled_rect,
.texture_level = 0,
};
unscaled_surface.Download(unscaled_download, staging);
}
@ -1224,10 +1211,10 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download,
r32_params.height = scaled_rect.GetHeight();
r32_params.type = VideoCore::SurfaceType::Color;
r32_params.res_scale = 1;
Surface r32_surface{r32_params, vk::Format::eR32Uint,
Surface r32_surface{*runtime, r32_params, vk::Format::eR32Uint,
vk::ImageUsageFlagBits::eTransferSrc |
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eStorage,
vk::ImageAspectFlagBits::eColor, runtime};
vk::ImageAspectFlagBits::eColor};
const VideoCore::TextureBlit blit = {
.src_level = download.texture_level,
@ -1237,8 +1224,7 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download,
.src_rect = scaled_rect,
.dst_rect = r32_scaled_rect,
};
runtime.blit_helper.BlitD24S8ToR32(*this, r32_surface, blit);
runtime->blit_helper.BlitD24S8ToR32(*this, r32_surface, blit);
// Blit the upper mip level to the lower one to scale without additional allocations
const bool is_scaled = res_scale != 1;
@ -1251,8 +1237,7 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download,
.src_rect = r32_scaled_rect,
.dst_rect = unscaled_rect,
};
runtime.BlitTextures(r32_surface, r32_surface, r32_blit);
runtime->BlitTextures(r32_surface, r32_surface, r32_blit);
}
const VideoCore::BufferTextureCopy r32_download = {
@ -1261,7 +1246,6 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download,
.texture_rect = unscaled_rect,
.texture_level = is_scaled ? 1u : 0u,
};
r32_surface.Download(r32_download, staging);
}
@ -1309,8 +1293,7 @@ void Framebuffer::PrepareImages(Surface* const color, Surface* const depth_stenc
Prepare(depth_stencil);
}
Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params)
: device{runtime.GetInstance().GetDevice()} {
Sampler::Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params) {
using TextureConfig = VideoCore::SamplerParams::TextureConfig;
const Instance& instance = runtime.GetInstance();
@ -1352,13 +1335,10 @@ Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params)
.unnormalizedCoordinates = false,
};
sampler = device.createSampler(sampler_info);
vk::Device device = runtime.GetInstance().GetDevice();
sampler = device.createSamplerUnique(sampler_info);
}
Sampler::~Sampler() {
if (sampler) {
device.destroySampler(sampler);
}
}
Sampler::~Sampler() = default;
} // namespace Vulkan

View File

@ -25,10 +25,10 @@ namespace Vulkan {
struct Allocation {
vk::Image image;
vk::ImageView image_view;
vk::ImageView depth_view;
vk::ImageView stencil_view;
vk::ImageView storage_view;
vk::UniqueImageView image_view;
vk::UniqueImageView depth_view;
vk::UniqueImageView stencil_view;
vk::UniqueImageView storage_view;
VmaAllocation allocation;
vk::ImageAspectFlags aspect;
vk::Format format;
@ -85,8 +85,8 @@ class TextureRuntime {
friend class Sampler;
public:
TextureRuntime(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorManager& desc_manager);
explicit TextureRuntime(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorManager& desc_manager);
~TextureRuntime();
/// Causes a GPU command flush
@ -98,7 +98,7 @@ public:
/// Takes back ownership of the allocation for recycling
void Recycle(const HostTextureTag tag, Allocation&& alloc);
/// Maps an internal staging buffer of the provided size of pixel uploads/downloads
/// Maps an internal staging buffer of the provided size for pixel uploads/downloads
[[nodiscard]] VideoCore::StagingData FindStaging(u32 size, bool upload);
/// Allocates a vulkan image possibly resusing an existing one
@ -163,11 +163,17 @@ class Surface : public VideoCore::SurfaceBase {
friend class TextureRuntime;
public:
Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime);
Surface(const VideoCore::SurfaceParams& params, vk::Format format, vk::ImageUsageFlags usage,
vk::ImageAspectFlags aspect, TextureRuntime& runtime);
Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params);
Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params, vk::Format format,
vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect);
~Surface();
Surface(const Surface&) = delete;
Surface& operator=(const Surface&) = delete;
Surface(Surface&& o) noexcept = default;
Surface& operator=(Surface&& o) noexcept = default;
/// Returns the surface aspect
vk::ImageAspectFlags Aspect() const noexcept {
return alloc.aspect;
@ -180,7 +186,7 @@ public:
/// Returns an image view used to sample the surface from a shader
vk::ImageView ImageView() const noexcept {
return alloc.image_view;
return alloc.image_view.get();
}
/// Uploads pixel data in staging to a rectangle region of the surface texture
@ -218,17 +224,17 @@ private:
/// Downloads scaled image by downscaling the requested rectangle
void ScaledDownload(const VideoCore::BufferTextureCopy& download,
const VideoCore::StagingData& stagings);
const VideoCore::StagingData& staging);
/// Downloads scaled depth stencil data
void DepthStencilDownload(const VideoCore::BufferTextureCopy& download,
const VideoCore::StagingData& staging);
private:
TextureRuntime& runtime;
const Instance& instance;
Scheduler& scheduler;
Allocation alloc;
TextureRuntime* runtime;
const Instance* instance;
Scheduler* scheduler;
Allocation alloc{};
bool is_framebuffer{};
bool is_storage{};
};
@ -282,34 +288,23 @@ private:
u32 height{};
};
/**
* @brief A sampler is used to configure the sampling parameters of a texture unit
*/
class Sampler {
public:
Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params);
Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params);
~Sampler();
Sampler(const Sampler&) = delete;
Sampler& operator=(const Sampler&) = delete;
Sampler(Sampler&& o) noexcept {
std::memcpy(this, &o, sizeof(Sampler));
o.sampler = VK_NULL_HANDLE;
}
Sampler& operator=(Sampler&& o) noexcept {
std::memcpy(this, &o, sizeof(Sampler));
o.sampler = VK_NULL_HANDLE;
return *this;
}
Sampler(Sampler&& o) noexcept = default;
Sampler& operator=(Sampler&& o) noexcept = default;
[[nodiscard]] vk::Sampler Handle() const noexcept {
return sampler;
return *sampler;
}
private:
vk::Device device;
vk::Sampler sampler;
vk::UniqueSampler sampler;
};
struct Traits {