rasterizer_cache: Move sampler management out of rasterizer cache
This commit is contained in:
@@ -41,6 +41,8 @@ add_library(video_core STATIC
|
|||||||
rasterizer_cache/rasterizer_cache.cpp
|
rasterizer_cache/rasterizer_cache.cpp
|
||||||
rasterizer_cache/rasterizer_cache.h
|
rasterizer_cache/rasterizer_cache.h
|
||||||
rasterizer_cache/rasterizer_cache_base.h
|
rasterizer_cache/rasterizer_cache_base.h
|
||||||
|
rasterizer_cache/sampler_params.h
|
||||||
|
rasterizer_cache/slot_vector.h
|
||||||
rasterizer_cache/surface_base.cpp
|
rasterizer_cache/surface_base.cpp
|
||||||
rasterizer_cache/surface_base.h
|
rasterizer_cache/surface_base.h
|
||||||
rasterizer_cache/surface_params.cpp
|
rasterizer_cache/surface_params.cpp
|
||||||
|
@@ -35,7 +35,18 @@ RasterizerCache<T>::RasterizerCache(Memory::MemorySystem& memory_,
|
|||||||
renderer{renderer_}, resolution_scale_factor{renderer.GetResolutionScaleFactor()},
|
renderer{renderer_}, resolution_scale_factor{renderer.GetResolutionScaleFactor()},
|
||||||
use_filter{Settings::values.texture_filter.GetValue() != Settings::TextureFilter::None},
|
use_filter{Settings::values.texture_filter.GetValue() != Settings::TextureFilter::None},
|
||||||
dump_textures{Settings::values.dump_textures.GetValue()},
|
dump_textures{Settings::values.dump_textures.GetValue()},
|
||||||
use_custom_textures{Settings::values.custom_textures.GetValue()} {}
|
use_custom_textures{Settings::values.custom_textures.GetValue()} {
|
||||||
|
using TextureConfig = Pica::TexturingRegs::TextureConfig;
|
||||||
|
|
||||||
|
// Create null handles for all cached resources
|
||||||
|
void(slot_samplers.insert(runtime, SamplerParams{
|
||||||
|
.mag_filter = TextureConfig::TextureFilter::Linear,
|
||||||
|
.min_filter = TextureConfig::TextureFilter::Linear,
|
||||||
|
.mip_filter = TextureConfig::TextureFilter::Linear,
|
||||||
|
.wrap_s = TextureConfig::WrapMode::ClampToBorder,
|
||||||
|
.wrap_t = TextureConfig::WrapMode::ClampToBorder,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
RasterizerCache<T>::~RasterizerCache() {
|
RasterizerCache<T>::~RasterizerCache() {
|
||||||
@@ -236,6 +247,34 @@ bool RasterizerCache<T>::AccelerateFill(const GPU::Regs::MemoryFillConfig& confi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
RasterizerCache<T>::Sampler& RasterizerCache<T>::GetSampler(SamplerId sampler_id) {
|
||||||
|
return slot_samplers[sampler_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
RasterizerCache<T>::Sampler& RasterizerCache<T>::GetSampler(
|
||||||
|
const Pica::TexturingRegs::TextureConfig& config) {
|
||||||
|
const SamplerParams params = {
|
||||||
|
.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 = config.lod.min_level,
|
||||||
|
.lod_max = config.lod.max_level,
|
||||||
|
.lod_bias = config.lod.bias,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto [it, is_new] = samplers.try_emplace(params);
|
||||||
|
if (is_new) {
|
||||||
|
it->second = slot_samplers.insert(runtime, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot_samplers[it->second];
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void RasterizerCache<T>::CopySurface(const SurfaceRef& src_surface, const SurfaceRef& dst_surface,
|
void RasterizerCache<T>::CopySurface(const SurfaceRef& src_surface, const SurfaceRef& dst_surface,
|
||||||
SurfaceInterval copy_interval) {
|
SurfaceInterval copy_interval) {
|
||||||
@@ -322,10 +361,8 @@ RasterizerCache<T>::SurfaceRect_Tuple RasterizerCache<T>::GetSurfaceSubRect(
|
|||||||
// Attempt to find encompassing surface
|
// Attempt to find encompassing surface
|
||||||
SurfaceRef surface = FindMatch<MatchFlags::SubRect>(params, match_res_scale);
|
SurfaceRef surface = FindMatch<MatchFlags::SubRect>(params, match_res_scale);
|
||||||
|
|
||||||
// Check if FindMatch failed because of res scaling
|
// Check if FindMatch failed because of res scaling. If that's the case create a new surface with
|
||||||
// If that's the case create a new surface with
|
// the dimensions of the lower res_scale surface to suggest it should not be used again.
|
||||||
// the dimensions of the lower res_scale surface
|
|
||||||
// to suggest it should not be used again
|
|
||||||
if (!surface && match_res_scale != ScaleMatch::Ignore) {
|
if (!surface && match_res_scale != ScaleMatch::Ignore) {
|
||||||
surface = FindMatch<MatchFlags::SubRect>(params, ScaleMatch::Ignore);
|
surface = FindMatch<MatchFlags::SubRect>(params, ScaleMatch::Ignore);
|
||||||
if (surface) {
|
if (surface) {
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/icl/interval_map.hpp>
|
#include <boost/icl/interval_map.hpp>
|
||||||
#include <tsl/robin_map.h>
|
#include <tsl/robin_map.h>
|
||||||
|
#include "video_core/rasterizer_cache/sampler_params.h"
|
||||||
#include "video_core/rasterizer_cache/surface_base.h"
|
#include "video_core/rasterizer_cache/surface_base.h"
|
||||||
|
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
@@ -51,6 +52,7 @@ class RasterizerCache {
|
|||||||
static constexpr u64 CITRA_PAGEBITS = 18;
|
static constexpr u64 CITRA_PAGEBITS = 18;
|
||||||
|
|
||||||
using TextureRuntime = typename T::TextureRuntime;
|
using TextureRuntime = typename T::TextureRuntime;
|
||||||
|
using Sampler = typename T::Sampler;
|
||||||
using SurfaceRef = std::shared_ptr<typename T::Surface>;
|
using SurfaceRef = std::shared_ptr<typename T::Surface>;
|
||||||
using Framebuffer = typename T::Framebuffer;
|
using Framebuffer = typename T::Framebuffer;
|
||||||
|
|
||||||
@@ -89,6 +91,10 @@ public:
|
|||||||
/// Perform hardware accelerated memory fill according to the provided configuration
|
/// Perform hardware accelerated memory fill according to the provided configuration
|
||||||
bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config);
|
bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config);
|
||||||
|
|
||||||
|
/// 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
|
/// Copy one surface's region to another
|
||||||
void CopySurface(const SurfaceRef& src_surface, const SurfaceRef& dst_surface,
|
void CopySurface(const SurfaceRef& src_surface, const SurfaceRef& dst_surface,
|
||||||
SurfaceInterval copy_interval);
|
SurfaceInterval copy_interval);
|
||||||
@@ -211,8 +217,10 @@ private:
|
|||||||
TextureRuntime& runtime;
|
TextureRuntime& runtime;
|
||||||
Pica::Regs& regs;
|
Pica::Regs& regs;
|
||||||
RendererBase& renderer;
|
RendererBase& renderer;
|
||||||
tsl::robin_pg_map<u64, std::vector<SurfaceRef>, Common::IdentityHash<u64>> page_table;
|
|
||||||
std::unordered_map<TextureCubeConfig, TextureCube> texture_cube_cache;
|
std::unordered_map<TextureCubeConfig, TextureCube> texture_cube_cache;
|
||||||
|
tsl::robin_pg_map<u64, std::vector<SurfaceRef>, Common::IdentityHash<u64>> page_table;
|
||||||
|
std::unordered_map<SamplerParams, SamplerId> samplers;
|
||||||
|
SlotVector<Sampler> slot_samplers;
|
||||||
SurfaceMap dirty_regions;
|
SurfaceMap dirty_regions;
|
||||||
PageMap cached_pages;
|
PageMap cached_pages;
|
||||||
std::vector<SurfaceRef> remove_surfaces;
|
std::vector<SurfaceRef> remove_surfaces;
|
||||||
|
42
src/video_core/rasterizer_cache/sampler_params.h
Normal file
42
src/video_core/rasterizer_cache/sampler_params.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/hash.h"
|
||||||
|
#include "video_core/regs_texturing.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
|
||||||
|
struct SamplerParams {
|
||||||
|
using TextureConfig = Pica::TexturingRegs::TextureConfig;
|
||||||
|
TextureConfig::TextureFilter mag_filter;
|
||||||
|
TextureConfig::TextureFilter min_filter;
|
||||||
|
TextureConfig::TextureFilter mip_filter;
|
||||||
|
TextureConfig::WrapMode wrap_s;
|
||||||
|
TextureConfig::WrapMode wrap_t;
|
||||||
|
u32 border_color = 0;
|
||||||
|
u32 lod_min = 0;
|
||||||
|
u32 lod_max = 0;
|
||||||
|
s32 lod_bias = 0;
|
||||||
|
|
||||||
|
auto operator<=>(const SamplerParams&) const noexcept = default;
|
||||||
|
|
||||||
|
const u64 Hash() const {
|
||||||
|
return Common::ComputeHash64(this, sizeof(SamplerParams));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<SamplerParams>,
|
||||||
|
"SamplerParams is not suitable for hashing");
|
||||||
|
|
||||||
|
} // namespace VideoCore
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<VideoCore::SamplerParams> {
|
||||||
|
std::size_t operator()(const VideoCore::SamplerParams& params) const noexcept {
|
||||||
|
return params.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
222
src/video_core/rasterizer_cache/slot_vector.h
Normal file
222
src/video_core/rasterizer_cache/slot_vector.h
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
#include <numeric>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
|
||||||
|
struct SlotId {
|
||||||
|
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
|
||||||
|
|
||||||
|
constexpr auto operator<=>(const SlotId&) const noexcept = default;
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const noexcept {
|
||||||
|
return index != INVALID_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 index = INVALID_INDEX;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class SlotVector {
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
friend SlotVector<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Iterator() = default;
|
||||||
|
|
||||||
|
Iterator& operator++() noexcept {
|
||||||
|
const u64* const bitset = slot_vector->stored_bitset.data();
|
||||||
|
const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
|
||||||
|
if (id.index < size) {
|
||||||
|
do {
|
||||||
|
++id.index;
|
||||||
|
} while (id.index < size && !IsValid(bitset));
|
||||||
|
if (id.index == size) {
|
||||||
|
id.index = SlotId::INVALID_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator operator++(int) noexcept {
|
||||||
|
const Iterator copy{*this};
|
||||||
|
++*this;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Iterator& other) const noexcept {
|
||||||
|
return id.index == other.id.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator& other) const noexcept {
|
||||||
|
return id.index != other.id.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<SlotId, T*> operator*() const noexcept {
|
||||||
|
return {id, std::addressof((*slot_vector)[id])};
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() const noexcept {
|
||||||
|
return std::addressof((*slot_vector)[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Iterator(SlotVector<T>* slot_vector_, SlotId id_) noexcept
|
||||||
|
: slot_vector{slot_vector_}, id{id_} {}
|
||||||
|
|
||||||
|
bool IsValid(const u64* bitset) const noexcept {
|
||||||
|
return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotVector<T>* slot_vector;
|
||||||
|
SlotId id;
|
||||||
|
};
|
||||||
|
|
||||||
|
~SlotVector() noexcept {
|
||||||
|
size_t index = 0;
|
||||||
|
for (u64 bits : stored_bitset) {
|
||||||
|
for (size_t bit = 0; bits; ++bit, bits >>= 1) {
|
||||||
|
if ((bits & 1) != 0) {
|
||||||
|
values[index + bit].object.~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += 64;
|
||||||
|
}
|
||||||
|
delete[] values;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] T& operator[](SlotId id) noexcept {
|
||||||
|
ValidateIndex(id);
|
||||||
|
return values[id.index].object;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const T& operator[](SlotId id) const noexcept {
|
||||||
|
ValidateIndex(id);
|
||||||
|
return values[id.index].object;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
[[nodiscard]] SlotId insert(Args&&... args) noexcept {
|
||||||
|
const u32 index = FreeValueIndex();
|
||||||
|
new (&values[index].object) T(std::forward<Args>(args)...);
|
||||||
|
SetStorageBit(index);
|
||||||
|
|
||||||
|
return SlotId{index};
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(SlotId id) noexcept {
|
||||||
|
values[id.index].object.~T();
|
||||||
|
free_list.push_back(id.index);
|
||||||
|
ResetStorageBit(id.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Iterator begin() noexcept {
|
||||||
|
const auto it = std::find_if(stored_bitset.begin(), stored_bitset.end(),
|
||||||
|
[](u64 value) { return value != 0; });
|
||||||
|
if (it == stored_bitset.end()) {
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
const u32 word_index = static_cast<u32>(std::distance(it, stored_bitset.begin()));
|
||||||
|
const SlotId first_id{word_index * 64 + static_cast<u32>(std::countr_zero(*it))};
|
||||||
|
return Iterator(this, first_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Iterator end() noexcept {
|
||||||
|
return Iterator(this, SlotId{SlotId::INVALID_INDEX});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct NonTrivialDummy {
|
||||||
|
NonTrivialDummy() noexcept {}
|
||||||
|
};
|
||||||
|
|
||||||
|
union Entry {
|
||||||
|
Entry() noexcept : dummy{} {}
|
||||||
|
~Entry() noexcept {}
|
||||||
|
|
||||||
|
NonTrivialDummy dummy;
|
||||||
|
T object;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetStorageBit(u32 index) noexcept {
|
||||||
|
stored_bitset[index / 64] |= u64(1) << (index % 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetStorageBit(u32 index) noexcept {
|
||||||
|
stored_bitset[index / 64] &= ~(u64(1) << (index % 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadStorageBit(u32 index) noexcept {
|
||||||
|
return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateIndex(SlotId id) const noexcept {
|
||||||
|
DEBUG_ASSERT(id);
|
||||||
|
DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
|
||||||
|
DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u32 FreeValueIndex() noexcept {
|
||||||
|
if (free_list.empty()) {
|
||||||
|
Reserve(values_capacity ? (values_capacity << 1) : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 free_index = free_list.back();
|
||||||
|
free_list.pop_back();
|
||||||
|
return free_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reserve(size_t new_capacity) noexcept {
|
||||||
|
Entry* const new_values = new Entry[new_capacity];
|
||||||
|
size_t index = 0;
|
||||||
|
for (u64 bits : stored_bitset) {
|
||||||
|
for (size_t bit = 0; bits; ++bit, bits >>= 1) {
|
||||||
|
const size_t i = index + bit;
|
||||||
|
if ((bits & 1) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
T& old_value = values[i].object;
|
||||||
|
new (&new_values[i].object) T(std::move(old_value));
|
||||||
|
old_value.~T();
|
||||||
|
}
|
||||||
|
index += 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
stored_bitset.resize((new_capacity + 63) / 64);
|
||||||
|
|
||||||
|
const size_t old_free_size = free_list.size();
|
||||||
|
free_list.resize(old_free_size + (new_capacity - values_capacity));
|
||||||
|
std::iota(free_list.begin() + old_free_size, free_list.end(),
|
||||||
|
static_cast<u32>(values_capacity));
|
||||||
|
|
||||||
|
delete[] values;
|
||||||
|
values = new_values;
|
||||||
|
values_capacity = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* values = nullptr;
|
||||||
|
size_t values_capacity = 0;
|
||||||
|
|
||||||
|
std::vector<u64> stored_bitset;
|
||||||
|
std::vector<u32> free_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCore
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::hash<VideoCore::SlotId> {
|
||||||
|
size_t operator()(const VideoCore::SlotId& id) const noexcept {
|
||||||
|
return std::hash<u32>{}(id.index);
|
||||||
|
}
|
||||||
|
};
|
@@ -11,11 +11,17 @@
|
|||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||||
|
#include "video_core/rasterizer_cache/slot_vector.h"
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
|
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
|
||||||
|
|
||||||
|
using SamplerId = SlotId;
|
||||||
|
|
||||||
|
/// Fake sampler ID for null samplers
|
||||||
|
constexpr SamplerId NULL_SAMPLER_ID{0};
|
||||||
|
|
||||||
struct Offset {
|
struct Offset {
|
||||||
constexpr auto operator<=>(const Offset&) const noexcept = default;
|
constexpr auto operator<=>(const Offset&) const noexcept = default;
|
||||||
|
|
||||||
|
@@ -97,16 +97,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory,
|
|||||||
u8 framebuffer_data[4] = {0, 0, 0, 1};
|
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);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
|
||||||
|
|
||||||
// Create sampler objects
|
|
||||||
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
|
|
||||||
texture_samplers[i].Create();
|
|
||||||
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create cubemap texture and sampler objects
|
|
||||||
texture_cube_sampler.Create();
|
|
||||||
state.texture_cube_unit.sampler = texture_cube_sampler.sampler.handle;
|
|
||||||
|
|
||||||
// Generate VAO
|
// Generate VAO
|
||||||
sw_vao.Create();
|
sw_vao.Create();
|
||||||
hw_vao.Create();
|
hw_vao.Create();
|
||||||
@@ -538,7 +528,8 @@ void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sync texture unit sampler
|
// Sync texture unit sampler
|
||||||
texture_samplers[texture_index].SyncWithConfig(texture.config);
|
const Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||||
|
state.texture_units[texture_index].sampler = sampler.Handle();
|
||||||
|
|
||||||
// Bind the texture provided by the rasterizer cache
|
// Bind the texture provided by the rasterizer cache
|
||||||
auto surface = res_cache.GetTextureSurface(texture);
|
auto surface = res_cache.GetTextureSurface(texture);
|
||||||
@@ -590,9 +581,10 @@ void RasterizerOpenGL::BindTextureCube(const Pica::TexturingRegs::FullTextureCon
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto surface = res_cache.GetTextureCube(config);
|
auto surface = res_cache.GetTextureCube(config);
|
||||||
texture_cube_sampler.SyncWithConfig(texture.config);
|
Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||||
|
|
||||||
state.texture_cube_unit.texture_cube = surface->Handle();
|
state.texture_cube_unit.texture_cube = surface->Handle();
|
||||||
|
state.texture_cube_unit.sampler = sampler.Handle();
|
||||||
state.texture_units[0].texture_2d = 0;
|
state.texture_units[0].texture_2d = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +600,7 @@ void RasterizerOpenGL::BindMaterial(u32 texture_index, Surface& surface) {
|
|||||||
glBindSampler(unit.id, sampler);
|
glBindSampler(unit.id, sampler);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GLuint sampler = texture_samplers[texture_index].sampler.handle;
|
const GLuint sampler = state.texture_units[texture_index].sampler;
|
||||||
if (surface.HasNormalMap()) {
|
if (surface.HasNormalMap()) {
|
||||||
if (regs.lighting.disable) {
|
if (regs.lighting.disable) {
|
||||||
LOG_WARNING(Render_OpenGL, "Custom normal map used but scene has no light enabled");
|
LOG_WARNING(Render_OpenGL, "Custom normal map used but scene has no light enabled");
|
||||||
@@ -798,64 +790,6 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SamplerInfo::Create() {
|
|
||||||
sampler.Create();
|
|
||||||
mag_filter = min_filter = mip_filter = TextureConfig::Linear;
|
|
||||||
wrap_s = wrap_t = TextureConfig::Repeat;
|
|
||||||
border_color = 0;
|
|
||||||
lod_min = lod_max = 0;
|
|
||||||
|
|
||||||
// default is 1000 and -1000
|
|
||||||
// Other attributes have correct defaults
|
|
||||||
glSamplerParameterf(sampler.handle, GL_TEXTURE_MAX_LOD, static_cast<float>(lod_max));
|
|
||||||
glSamplerParameterf(sampler.handle, GL_TEXTURE_MIN_LOD, static_cast<float>(lod_min));
|
|
||||||
}
|
|
||||||
|
|
||||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
|
||||||
const Pica::TexturingRegs::TextureConfig& config) {
|
|
||||||
|
|
||||||
GLuint s = sampler.handle;
|
|
||||||
|
|
||||||
if (mag_filter != config.mag_filter) {
|
|
||||||
mag_filter = config.mag_filter;
|
|
||||||
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureMagFilterMode(mag_filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (min_filter != config.min_filter || mip_filter != config.mip_filter) {
|
|
||||||
min_filter = config.min_filter;
|
|
||||||
mip_filter = config.mip_filter;
|
|
||||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
|
||||||
PicaToGL::TextureMinFilterMode(min_filter, mip_filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wrap_s != config.wrap_s) {
|
|
||||||
wrap_s = config.wrap_s;
|
|
||||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s));
|
|
||||||
}
|
|
||||||
if (wrap_t != config.wrap_t) {
|
|
||||||
wrap_t = config.wrap_t;
|
|
||||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(wrap_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wrap_s == TextureConfig::ClampToBorder || wrap_t == TextureConfig::ClampToBorder) {
|
|
||||||
if (border_color != config.border_color.raw) {
|
|
||||||
border_color = config.border_color.raw;
|
|
||||||
auto gl_color = PicaToGL::ColorRGBA8(border_color);
|
|
||||||
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.AsArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lod_min != config.lod.min_level) {
|
|
||||||
lod_min = config.lod.min_level;
|
|
||||||
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, static_cast<float>(lod_min));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lod_max != config.lod.max_level) {
|
|
||||||
lod_max = config.lod.max_level;
|
|
||||||
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, static_cast<float>(lod_max));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RasterizerOpenGL::SyncClipEnabled() {
|
void RasterizerOpenGL::SyncClipEnabled() {
|
||||||
state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
|
state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
|
||||||
}
|
}
|
||||||
|
@@ -55,28 +55,6 @@ private:
|
|||||||
void SyncFixedState() override;
|
void SyncFixedState() override;
|
||||||
void NotifyFixedFunctionPicaRegisterChanged(u32 id) override;
|
void NotifyFixedFunctionPicaRegisterChanged(u32 id) override;
|
||||||
|
|
||||||
struct SamplerInfo {
|
|
||||||
using TextureConfig = Pica::TexturingRegs::TextureConfig;
|
|
||||||
|
|
||||||
OGLSampler sampler;
|
|
||||||
|
|
||||||
/// Creates the sampler object, initializing its state so that it's in sync with the
|
|
||||||
/// SamplerInfo struct.
|
|
||||||
void Create();
|
|
||||||
/// Syncs the sampler object with the config, updating any necessary state.
|
|
||||||
void SyncWithConfig(const TextureConfig& config);
|
|
||||||
|
|
||||||
private:
|
|
||||||
TextureConfig::TextureFilter mag_filter;
|
|
||||||
TextureConfig::TextureFilter min_filter;
|
|
||||||
TextureConfig::TextureFilter mip_filter;
|
|
||||||
TextureConfig::WrapMode wrap_s;
|
|
||||||
TextureConfig::WrapMode wrap_t;
|
|
||||||
u32 border_color;
|
|
||||||
u32 lod_min;
|
|
||||||
u32 lod_max;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Syncs the clip enabled status to match the PICA register
|
/// Syncs the clip enabled status to match the PICA register
|
||||||
void SyncClipEnabled();
|
void SyncClipEnabled();
|
||||||
|
|
||||||
@@ -163,7 +141,6 @@ private:
|
|||||||
OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
|
OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
|
||||||
std::array<bool, 16> hw_vao_enabled_attributes{};
|
std::array<bool, 16> hw_vao_enabled_attributes{};
|
||||||
|
|
||||||
std::array<SamplerInfo, 3> texture_samplers;
|
|
||||||
GLsizeiptr texture_buffer_size;
|
GLsizeiptr texture_buffer_size;
|
||||||
OGLStreamBuffer vertex_buffer;
|
OGLStreamBuffer vertex_buffer;
|
||||||
OGLStreamBuffer uniform_buffer;
|
OGLStreamBuffer uniform_buffer;
|
||||||
@@ -175,8 +152,6 @@ private:
|
|||||||
std::size_t uniform_size_aligned_vs;
|
std::size_t uniform_size_aligned_vs;
|
||||||
std::size_t uniform_size_aligned_fs;
|
std::size_t uniform_size_aligned_fs;
|
||||||
|
|
||||||
SamplerInfo texture_cube_sampler;
|
|
||||||
|
|
||||||
OGLTexture texture_buffer_lut_lf;
|
OGLTexture texture_buffer_lut_lf;
|
||||||
OGLTexture texture_buffer_lut_rg;
|
OGLTexture texture_buffer_lut_rg;
|
||||||
OGLTexture texture_buffer_lut_rgba;
|
OGLTexture texture_buffer_lut_rgba;
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
#include "video_core/renderer_opengl/gl_driver.h"
|
#include "video_core/renderer_opengl/gl_driver.h"
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||||
|
#include "video_core/renderer_opengl/pica_to_gl.h"
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
@@ -692,4 +693,30 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color, u32 colo
|
|||||||
|
|
||||||
Framebuffer::~Framebuffer() = default;
|
Framebuffer::~Framebuffer() = default;
|
||||||
|
|
||||||
|
Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params) {
|
||||||
|
const GLenum mag_filter = PicaToGL::TextureMagFilterMode(params.mag_filter);
|
||||||
|
const GLenum min_filter = PicaToGL::TextureMinFilterMode(params.min_filter, params.mip_filter);
|
||||||
|
const GLenum wrap_s = PicaToGL::WrapMode(params.wrap_s);
|
||||||
|
const GLenum wrap_t = PicaToGL::WrapMode(params.wrap_t);
|
||||||
|
const Common::Vec4f gl_color = PicaToGL::ColorRGBA8(params.border_color);
|
||||||
|
const float lod_min = params.lod_min;
|
||||||
|
const float lod_max = params.lod_max;
|
||||||
|
|
||||||
|
sampler.Create();
|
||||||
|
|
||||||
|
const GLuint handle = sampler.handle;
|
||||||
|
glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||||
|
glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||||
|
|
||||||
|
glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, wrap_s);
|
||||||
|
glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, wrap_t);
|
||||||
|
|
||||||
|
glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, gl_color.AsArray());
|
||||||
|
|
||||||
|
glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, lod_min);
|
||||||
|
glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, lod_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sampler::~Sampler() = default;
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
@@ -223,8 +223,28 @@ private:
|
|||||||
GLuint handle{};
|
GLuint handle{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Sampler {
|
||||||
|
public:
|
||||||
|
explicit Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params);
|
||||||
|
~Sampler();
|
||||||
|
|
||||||
|
Sampler(const Sampler&) = delete;
|
||||||
|
Sampler& operator=(const Sampler&) = delete;
|
||||||
|
|
||||||
|
Sampler(Sampler&&) = default;
|
||||||
|
Sampler& operator=(Sampler&&) = default;
|
||||||
|
|
||||||
|
[[nodiscard]] GLuint Handle() const noexcept {
|
||||||
|
return sampler.handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OGLSampler sampler;
|
||||||
|
};
|
||||||
|
|
||||||
struct Traits {
|
struct Traits {
|
||||||
using TextureRuntime = OpenGL::TextureRuntime;
|
using TextureRuntime = OpenGL::TextureRuntime;
|
||||||
|
using Sampler = OpenGL::Sampler;
|
||||||
using Surface = OpenGL::Surface;
|
using Surface = OpenGL::Surface;
|
||||||
using Framebuffer = OpenGL::Framebuffer;
|
using Framebuffer = OpenGL::Framebuffer;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user