Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3ec288568 | |||
| 58659a702d | |||
| b65871407e | |||
| f0161d2799 | |||
| 6728b9b252 | |||
| 7ac5d2bac8 | |||
| 256caec9ef | |||
| f733682aa7 | |||
| e6e75f1c12 | |||
| 922c7f4e51 | |||
| ce3edaad26 | |||
| 573a1e7662 | |||
| e1981b8b8d | |||
| 19af91434e | |||
| 81fbc5370d | |||
| d4f33b822b | |||
| 137d165672 | |||
| 86b39e0677 | |||
| 7397289266 | |||
| 952f010c2c | |||
| 4ea572791b | |||
| 52a41f482f | |||
| e3534700d7 | |||
| b13fbc25b8 | |||
| 6207751b00 | |||
| 80702aa88f | |||
| 9cdf5c6c31 | |||
| 8ad7268c75 | |||
| 9a76e94b3d | |||
| 37a352e9d3 |
@@ -10,7 +10,7 @@ jobs:
|
||||
BuildSuffix: 'windows-testing'
|
||||
ScriptFolder: 'windows'
|
||||
steps:
|
||||
- script: sudo apt upgrade python3-pip && pip install requests urllib3
|
||||
- script: sudo apt-get update && sudo apt-get --only-upgrade -y install python3-pip && pip install requests urllib3
|
||||
displayName: 'Prepare Environment'
|
||||
- task: PythonScript@0
|
||||
condition: eq(variables['Build.Reason'], 'PullRequest')
|
||||
|
||||
@@ -175,6 +175,10 @@ MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source) {
|
||||
if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) {
|
||||
return os << "[UNKNOWN SOURCE]";
|
||||
}
|
||||
|
||||
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
transform, crop_rect};
|
||||
|
||||
system.GetPerfStats().EndGameFrame();
|
||||
system.GPU().SwapBuffers(framebuffer);
|
||||
system.GPU().SwapBuffers(&framebuffer);
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -64,6 +64,8 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_shader_manager.h
|
||||
renderer_opengl/gl_shader_util.cpp
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_staging_buffer.cpp
|
||||
renderer_opengl/gl_staging_buffer.h
|
||||
renderer_opengl/gl_state.cpp
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/gl_stream_buffer.cpp
|
||||
@@ -114,6 +116,7 @@ add_library(video_core STATIC
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
staging_buffer_cache.h
|
||||
surface.cpp
|
||||
surface.h
|
||||
texture_cache/surface_base.cpp
|
||||
|
||||
@@ -249,16 +249,10 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
executing_macro = 0;
|
||||
|
||||
// Lookup the macro offset
|
||||
const u32 entry{(method - MacroRegistersStart) >> 1};
|
||||
const auto& search{macro_offsets.find(entry)};
|
||||
if (search == macro_offsets.end()) {
|
||||
LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
const u32 entry = ((method - MacroRegistersStart) >> 1) % macro_positions.size();
|
||||
|
||||
// Execute the current macro.
|
||||
macro_interpreter.Execute(search->second, std::move(parameters));
|
||||
macro_interpreter.Execute(macro_positions[entry], std::move(parameters));
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
@@ -421,7 +415,7 @@ void Maxwell3D::ProcessMacroUpload(u32 data) {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacroBind(u32 data) {
|
||||
macro_offsets[regs.macros.entry] = data;
|
||||
macro_positions[regs.macros.entry++] = data;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
|
||||
@@ -1270,7 +1270,7 @@ private:
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
/// Start offsets of each macro in macro_memory
|
||||
std::unordered_map<u32, u32> macro_offsets;
|
||||
std::array<u32, 0x80> macro_positions = {};
|
||||
|
||||
/// Memory for macro code
|
||||
MacroMemory macro_memory;
|
||||
|
||||
@@ -886,6 +886,7 @@ union Instruction {
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
BitField<6, 1, u64> neg_b;
|
||||
BitField<7, 1, u64> abs_a;
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
@@ -1019,7 +1020,6 @@ union Instruction {
|
||||
} iset;
|
||||
|
||||
union {
|
||||
BitField<41, 2, u64> selector; // i2i and i2f only
|
||||
BitField<45, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_a;
|
||||
BitField<10, 2, Register::Size> src_size;
|
||||
@@ -1045,6 +1045,13 @@ union Instruction {
|
||||
}
|
||||
} f2f;
|
||||
|
||||
union {
|
||||
BitField<41, 2, u64> selector;
|
||||
} int_src;
|
||||
|
||||
union {
|
||||
BitField<41, 1, u64> selector;
|
||||
} float_src;
|
||||
} conversion;
|
||||
|
||||
union {
|
||||
|
||||
@@ -17,18 +17,6 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
case PixelFormat::BGRA8:
|
||||
return 4;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
|
||||
: system{system}, renderer{renderer}, is_async{is_async} {
|
||||
auto& rasterizer{renderer.Rasterizer()};
|
||||
|
||||
@@ -95,14 +95,10 @@ class DebugContext;
|
||||
struct FramebufferConfig {
|
||||
enum class PixelFormat : u32 {
|
||||
ABGR8 = 1,
|
||||
RGB565 = 4,
|
||||
BGRA8 = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of bytes per pixel.
|
||||
*/
|
||||
static u32 BytesPerPixel(PixelFormat format);
|
||||
|
||||
VAddr address;
|
||||
u32 offset;
|
||||
u32 width;
|
||||
@@ -253,8 +249,7 @@ public:
|
||||
virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
virtual void SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
|
||||
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||
virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
|
||||
|
||||
@@ -23,9 +23,8 @@ void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||
gpu_thread.SubmitList(std::move(entries));
|
||||
}
|
||||
|
||||
void GPUAsynch::SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
|
||||
gpu_thread.SwapBuffers(std::move(framebuffer));
|
||||
void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
gpu_thread.SwapBuffers(framebuffer);
|
||||
}
|
||||
|
||||
void GPUAsynch::FlushRegion(CacheAddr addr, u64 size) {
|
||||
|
||||
@@ -14,15 +14,14 @@ class RendererBase;
|
||||
namespace VideoCommon {
|
||||
|
||||
/// Implementation of GPU interface that runs the GPU asynchronously
|
||||
class GPUAsynch : public Tegra::GPU {
|
||||
class GPUAsynch final : public Tegra::GPU {
|
||||
public:
|
||||
explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
|
||||
~GPUAsynch() override;
|
||||
|
||||
void Start() override;
|
||||
void PushGPUEntries(Tegra::CommandList&& entries) override;
|
||||
void SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||
void FlushRegion(CacheAddr addr, u64 size) override;
|
||||
void InvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
|
||||
@@ -19,9 +19,8 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||
dma_pusher->DispatchCalls();
|
||||
}
|
||||
|
||||
void GPUSynch::SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
|
||||
renderer.SwapBuffers(std::move(framebuffer));
|
||||
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
renderer.SwapBuffers(framebuffer);
|
||||
}
|
||||
|
||||
void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
|
||||
|
||||
@@ -13,15 +13,14 @@ class RendererBase;
|
||||
namespace VideoCommon {
|
||||
|
||||
/// Implementation of GPU interface that runs the GPU synchronously
|
||||
class GPUSynch : public Tegra::GPU {
|
||||
class GPUSynch final : public Tegra::GPU {
|
||||
public:
|
||||
explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
|
||||
~GPUSynch() override;
|
||||
|
||||
void Start() override;
|
||||
void PushGPUEntries(Tegra::CommandList&& entries) override;
|
||||
void SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||
void FlushRegion(CacheAddr addr, u64 size) override;
|
||||
void InvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
|
||||
@@ -39,7 +39,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
||||
dma_pusher.Push(std::move(submit_list->entries));
|
||||
dma_pusher.DispatchCalls();
|
||||
} else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
|
||||
renderer.SwapBuffers(std::move(data->framebuffer));
|
||||
renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
|
||||
} else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
|
||||
renderer.Rasterizer().FlushRegion(data->addr, data->size);
|
||||
} else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
|
||||
@@ -78,9 +78,9 @@ void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
|
||||
}
|
||||
|
||||
void ThreadManager::SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
|
||||
PushCommand(SwapBuffersCommand(std::move(framebuffer)));
|
||||
void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
PushCommand(SwapBuffersCommand(framebuffer ? *framebuffer
|
||||
: std::optional<const Tegra::FramebufferConfig>{}));
|
||||
}
|
||||
|
||||
void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
|
||||
|
||||
@@ -110,8 +110,7 @@ public:
|
||||
void SubmitList(Tegra::CommandList&& entries);
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||
void FlushRegion(CacheAddr addr, u64 size);
|
||||
|
||||
+2
-114
@@ -25,8 +25,8 @@ static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth
|
||||
|
||||
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
|
||||
// pixel values.
|
||||
const u32 tile_size_x{GetDefaultBlockWidth(format)};
|
||||
const u32 tile_size_y{GetDefaultBlockHeight(format)};
|
||||
constexpr u32 tile_size_x{GetDefaultBlockWidth(format)};
|
||||
constexpr u32 tile_size_y{GetDefaultBlockHeight(format)};
|
||||
|
||||
if constexpr (morton_to_linear) {
|
||||
Tegra::Texture::UnswizzleTexture(buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
|
||||
@@ -186,99 +186,6 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
|
||||
return morton_to_linear_fns[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
static u32 MortonInterleave128(u32 x, u32 y) {
|
||||
// 128x128 Z-Order coordinate from 2D coordinates
|
||||
static constexpr u32 xlut[] = {
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042,
|
||||
0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809,
|
||||
0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000,
|
||||
0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043,
|
||||
0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a,
|
||||
0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001,
|
||||
0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048,
|
||||
0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b,
|
||||
0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002,
|
||||
0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049,
|
||||
0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840,
|
||||
0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003,
|
||||
0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a,
|
||||
0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841,
|
||||
0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008,
|
||||
0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b,
|
||||
0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842,
|
||||
0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009,
|
||||
0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800,
|
||||
0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843,
|
||||
0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a,
|
||||
0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801,
|
||||
0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848,
|
||||
0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802,
|
||||
0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849,
|
||||
0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040,
|
||||
0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803,
|
||||
0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a,
|
||||
0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041,
|
||||
0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808,
|
||||
0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b,
|
||||
0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042,
|
||||
0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809,
|
||||
0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b,
|
||||
};
|
||||
static constexpr u32 ylut[] = {
|
||||
0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090,
|
||||
0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124,
|
||||
0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200,
|
||||
0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294,
|
||||
0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330,
|
||||
0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404,
|
||||
0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0,
|
||||
0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534,
|
||||
0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610,
|
||||
0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4,
|
||||
0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780,
|
||||
0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014,
|
||||
0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0,
|
||||
0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184,
|
||||
0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220,
|
||||
0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4,
|
||||
0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390,
|
||||
0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424,
|
||||
0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500,
|
||||
0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594,
|
||||
0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630,
|
||||
0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704,
|
||||
0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0,
|
||||
0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034,
|
||||
0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110,
|
||||
0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4,
|
||||
0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280,
|
||||
0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314,
|
||||
0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0,
|
||||
0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484,
|
||||
0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520,
|
||||
0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4,
|
||||
0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690,
|
||||
0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724,
|
||||
0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4,
|
||||
};
|
||||
return xlut[x % 128] + ylut[y % 128];
|
||||
}
|
||||
|
||||
static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
|
||||
// Calculates the offset of the position of the pixel in Morton order
|
||||
// Framebuffer images are split into 128x128 tiles.
|
||||
|
||||
constexpr u32 block_height = 128;
|
||||
const u32 coarse_x = x & ~127;
|
||||
|
||||
const u32 i = MortonInterleave128(x, y);
|
||||
|
||||
const u32 offset = coarse_x * block_height;
|
||||
|
||||
return (i + offset) * bytes_per_pixel;
|
||||
}
|
||||
|
||||
void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
|
||||
u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
|
||||
u8* buffer, u8* addr) {
|
||||
@@ -286,23 +193,4 @@ void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stri
|
||||
tile_width_spacing, buffer, addr);
|
||||
}
|
||||
|
||||
void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
|
||||
u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data) {
|
||||
const bool morton_to_linear = mode == MortonSwizzleMode::MortonToLinear;
|
||||
u8* data_ptrs[2];
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
for (u32 x = 0; x < width; ++x) {
|
||||
const u32 coarse_y = y & ~127;
|
||||
const u32 morton_offset =
|
||||
GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel;
|
||||
const u32 linear_pixel_index = (x + y * width) * linear_bytes_per_pixel;
|
||||
|
||||
data_ptrs[morton_to_linear ? 1 : 0] = morton_data + morton_offset;
|
||||
data_ptrs[morton_to_linear ? 0 : 1] = &linear_data[linear_pixel_index];
|
||||
|
||||
std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -15,7 +15,4 @@ void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat forma
|
||||
u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
|
||||
u8* buffer, u8* addr);
|
||||
|
||||
void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
|
||||
u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -36,8 +36,7 @@ public:
|
||||
virtual ~RendererBase();
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
virtual void SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
|
||||
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
|
||||
|
||||
/// Initialize the renderer
|
||||
virtual bool Init() = 0;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
@@ -23,6 +25,9 @@ T GetInteger(GLenum pname) {
|
||||
} // Anonymous namespace
|
||||
|
||||
Device::Device() {
|
||||
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const bool intel_proprietary = vendor == "Intel";
|
||||
|
||||
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
||||
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
||||
@@ -32,6 +37,7 @@ Device::Device() {
|
||||
has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = TestComponentIndexingBug();
|
||||
has_broken_pbo_streaming = intel_proprietary;
|
||||
}
|
||||
|
||||
Device::Device(std::nullptr_t) {
|
||||
@@ -42,6 +48,7 @@ Device::Device(std::nullptr_t) {
|
||||
has_vertex_viewport_layer = true;
|
||||
has_variable_aoffi = true;
|
||||
has_component_indexing_bug = false;
|
||||
has_broken_pbo_streaming = false;
|
||||
}
|
||||
|
||||
bool Device::TestVariableAoffi() {
|
||||
|
||||
@@ -46,6 +46,10 @@ public:
|
||||
return has_component_indexing_bug;
|
||||
}
|
||||
|
||||
bool HasBrokenPBOStreaming() const {
|
||||
return has_broken_pbo_streaming;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestComponentIndexingBug();
|
||||
@@ -58,6 +62,7 @@ private:
|
||||
bool has_vertex_viewport_layer{};
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
bool has_broken_pbo_streaming{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_staging_buffer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class PersistentStagingBuffer final : public StagingBuffer {
|
||||
public:
|
||||
explicit PersistentStagingBuffer(std::size_t size, bool is_read_buffer)
|
||||
: is_read_buffer{is_read_buffer} {
|
||||
constexpr GLenum storage_read =
|
||||
GL_CLIENT_STORAGE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT;
|
||||
constexpr GLenum storage_write =
|
||||
GL_CLIENT_STORAGE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT;
|
||||
constexpr GLenum map_read = GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT;
|
||||
constexpr GLenum map_write = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT |
|
||||
GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
||||
const GLenum storage = is_read_buffer ? storage_read : storage_write;
|
||||
const GLenum map = is_read_buffer ? map_read : map_write;
|
||||
|
||||
buffer.Create();
|
||||
glNamedBufferStorage(buffer.handle, static_cast<GLsizeiptr>(size), nullptr, storage);
|
||||
pointer = reinterpret_cast<u8*>(
|
||||
glMapNamedBufferRange(buffer.handle, 0, static_cast<GLsizeiptr>(size), map));
|
||||
}
|
||||
|
||||
~PersistentStagingBuffer() override {
|
||||
if (sync) {
|
||||
glDeleteSync(sync);
|
||||
}
|
||||
}
|
||||
|
||||
u8* GetOpenGLPointer() const override {
|
||||
// Operations with a bound OpenGL buffer start with an offset of 0.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u8* Map([[maybe_unused]] std::size_t size) const override {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
void Unmap(std::size_t size) const override {
|
||||
if (!is_read_buffer) {
|
||||
// We flush the buffer on write operations
|
||||
glFlushMappedNamedBufferRange(buffer.handle, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueFence(bool own) override {
|
||||
DEBUG_ASSERT(!sync);
|
||||
owned = own;
|
||||
sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
void WaitFence() override {
|
||||
DEBUG_ASSERT(sync);
|
||||
switch (glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED)) {
|
||||
case GL_ALREADY_SIGNALED:
|
||||
case GL_CONDITION_SATISFIED:
|
||||
break;
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
case GL_WAIT_FAILED:
|
||||
UNREACHABLE_MSG("Fence wait failed");
|
||||
break;
|
||||
}
|
||||
Discard();
|
||||
}
|
||||
|
||||
void Discard() override {
|
||||
DEBUG_ASSERT(sync);
|
||||
glDeleteSync(sync);
|
||||
sync = nullptr;
|
||||
owned = false;
|
||||
}
|
||||
|
||||
bool IsAvailable() override {
|
||||
if (owned) {
|
||||
return false;
|
||||
}
|
||||
if (!sync) {
|
||||
return true;
|
||||
}
|
||||
GLint status;
|
||||
glGetSynciv(sync, GL_SYNC_STATUS, sizeof(GLint), nullptr, &status);
|
||||
if (status == GL_UNSIGNALED) {
|
||||
return false;
|
||||
}
|
||||
// The fence has been signaled, we can destroy it
|
||||
glDeleteSync(sync);
|
||||
sync = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bind(GLenum target) const override {
|
||||
glBindBuffer(target, buffer.handle);
|
||||
}
|
||||
|
||||
private:
|
||||
OGLBuffer buffer;
|
||||
GLsync sync{};
|
||||
u8* pointer{};
|
||||
bool is_read_buffer{};
|
||||
bool owned{};
|
||||
};
|
||||
|
||||
class CpuStagingBuffer final : public StagingBuffer {
|
||||
public:
|
||||
explicit CpuStagingBuffer(std::size_t size) : pointer{std::make_unique<u8[]>(size)} {}
|
||||
|
||||
~CpuStagingBuffer() override = default;
|
||||
|
||||
u8* Map([[maybe_unused]] std::size_t size) const override {
|
||||
return pointer.get();
|
||||
}
|
||||
|
||||
u8* GetOpenGLPointer() const override {
|
||||
return pointer.get();
|
||||
}
|
||||
|
||||
void Unmap([[maybe_unused]] std::size_t size) const override {}
|
||||
|
||||
void QueueFence(bool own) override {
|
||||
// We don't queue anything here
|
||||
}
|
||||
|
||||
void WaitFence() override {
|
||||
// CPU operations are immediate, we don't wait for anything
|
||||
}
|
||||
|
||||
void Discard() override {
|
||||
UNREACHABLE_MSG("CpuStagingBuffer doesn't support deferred operations");
|
||||
}
|
||||
|
||||
bool IsAvailable() override {
|
||||
// A CPU buffer is always available, operations are immediate
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bind(GLenum target) const override {
|
||||
// OpenGL operations that use CPU buffers need that the target is zero
|
||||
glBindBuffer(target, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<u8[]> pointer;
|
||||
};
|
||||
|
||||
StagingBufferCache::StagingBufferCache(const Device& device)
|
||||
: VideoCommon::StagingBufferCache<StagingBuffer>{!device.HasBrokenPBOStreaming()},
|
||||
device{device} {}
|
||||
|
||||
StagingBufferCache::~StagingBufferCache() = default;
|
||||
|
||||
std::unique_ptr<StagingBuffer> StagingBufferCache::CreateBuffer(std::size_t size, bool is_flush) {
|
||||
if (device.HasBrokenPBOStreaming()) {
|
||||
return std::unique_ptr<StagingBuffer>(new CpuStagingBuffer(size));
|
||||
} else {
|
||||
return std::unique_ptr<StagingBuffer>(new PersistentStagingBuffer(size, is_flush));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/staging_buffer_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Device;
|
||||
class StagingBuffer;
|
||||
|
||||
class StagingBufferCache final : public VideoCommon::StagingBufferCache<StagingBuffer> {
|
||||
public:
|
||||
explicit StagingBufferCache(const Device& device);
|
||||
~StagingBufferCache() override;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<StagingBuffer> CreateBuffer(std::size_t size, bool is_flush) override;
|
||||
|
||||
private:
|
||||
const Device& device;
|
||||
};
|
||||
|
||||
class StagingBuffer : public NonCopyable {
|
||||
public:
|
||||
virtual ~StagingBuffer() = default;
|
||||
|
||||
/// Returns the base pointer passed to an OpenGL function.
|
||||
[[nodiscard]] virtual u8* GetOpenGLPointer() const = 0;
|
||||
|
||||
/// Maps the staging buffer.
|
||||
[[nodiscard]] virtual u8* Map(std::size_t size) const = 0;
|
||||
|
||||
/// Unmaps the staging buffer
|
||||
virtual void Unmap(std::size_t size) const = 0;
|
||||
|
||||
/// Inserts a fence in the OpenGL pipeline.
|
||||
/// @param own Protects the fence from being used before it's waited, intended for flushes.
|
||||
virtual void QueueFence(bool own) = 0;
|
||||
|
||||
/// Waits for a fence and releases the ownership.
|
||||
virtual void WaitFence() = 0;
|
||||
|
||||
/// Discards the deferred operation and its bound fence. A fence must be queued.
|
||||
virtual void Discard() = 0;
|
||||
|
||||
/// Returns true when the fence is available.
|
||||
[[nodiscard]] virtual bool IsAvailable() = 0;
|
||||
|
||||
/// Binds the staging buffer handle to an OpenGL target.
|
||||
virtual void Bind(GLenum target) const = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -10,9 +10,11 @@
|
||||
#include "core/core.h"
|
||||
#include "video_core/morton.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_staging_buffer.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/staging_buffer_cache.h"
|
||||
#include "video_core/texture_cache/surface_base.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
#include "video_core/textures/convert.h"
|
||||
@@ -234,8 +236,9 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: VideoCommon::SurfaceBase<View>(gpu_addr, params) {
|
||||
CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
std::vector<u8>& temporary_buffer)
|
||||
: VideoCommon::SurfaceBase<View, StagingBuffer>{gpu_addr, params, temporary_buffer} {
|
||||
const auto& tuple{GetFormatTuple(params.pixel_format, params.component_type)};
|
||||
internal_format = tuple.internal_format;
|
||||
format = tuple.format;
|
||||
@@ -251,45 +254,52 @@ CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& param
|
||||
|
||||
CachedSurface::~CachedSurface() = default;
|
||||
|
||||
void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
|
||||
void CachedSurface::DownloadTexture(StagingBuffer& buffer) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture_Download);
|
||||
|
||||
SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
|
||||
|
||||
buffer.Bind(GL_PIXEL_PACK_BUFFER);
|
||||
const auto pointer_base = buffer.GetOpenGLPointer();
|
||||
for (u32 level = 0; level < params.emulated_levels; ++level) {
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
|
||||
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level);
|
||||
const auto mip_offset = pointer_base + params.GetHostMipmapLevelOffset(level);
|
||||
if (is_compressed) {
|
||||
glGetCompressedTextureImage(texture.handle, level,
|
||||
static_cast<GLsizei>(params.GetHostMipmapSize(level)),
|
||||
staging_buffer.data() + mip_offset);
|
||||
mip_offset);
|
||||
} else {
|
||||
glGetTextureImage(texture.handle, level, format, type,
|
||||
static_cast<GLsizei>(params.GetHostMipmapSize(level)),
|
||||
staging_buffer.data() + mip_offset);
|
||||
static_cast<GLsizei>(params.GetHostMipmapSize(level)), mip_offset);
|
||||
}
|
||||
}
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
|
||||
// According to Cemu glGetTextureImage and friends do not flush, resulting in a softlock if we
|
||||
// wait for a fence. To fix this we have to explicitly flush and then queue a fence.
|
||||
glFlush();
|
||||
buffer.QueueFence(true);
|
||||
}
|
||||
|
||||
void CachedSurface::UploadTexture(const std::vector<u8>& staging_buffer) {
|
||||
void CachedSurface::UploadTexture(StagingBuffer& buffer) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture_Upload);
|
||||
SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); });
|
||||
|
||||
buffer.Bind(GL_PIXEL_UNPACK_BUFFER);
|
||||
const auto pointer = buffer.GetOpenGLPointer();
|
||||
for (u32 level = 0; level < params.emulated_levels; ++level) {
|
||||
UploadTextureMipmap(level, staging_buffer);
|
||||
UploadTextureMipmap(level, pointer);
|
||||
}
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
buffer.QueueFence(false);
|
||||
}
|
||||
|
||||
void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& staging_buffer) {
|
||||
void CachedSurface::UploadTextureMipmap(u32 level, const u8* opengl_pointer) {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
|
||||
|
||||
auto compression_type = params.GetCompressionType();
|
||||
|
||||
const std::size_t mip_offset = compression_type == SurfaceCompression::Converted
|
||||
? params.GetConvertedMipmapOffset(level)
|
||||
: params.GetHostMipmapLevelOffset(level);
|
||||
const u8* buffer{staging_buffer.data() + mip_offset};
|
||||
const auto compression_type = params.GetCompressionType();
|
||||
const u8* mip_offset = opengl_pointer + (compression_type == SurfaceCompression::Converted
|
||||
? params.GetConvertedMipmapOffset(level)
|
||||
: params.GetHostMipmapLevelOffset(level));
|
||||
if (is_compressed) {
|
||||
const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))};
|
||||
switch (params.target) {
|
||||
@@ -297,7 +307,7 @@ void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& stagin
|
||||
glCompressedTextureSubImage2D(texture.handle, level, 0, 0,
|
||||
static_cast<GLsizei>(params.GetMipWidth(level)),
|
||||
static_cast<GLsizei>(params.GetMipHeight(level)),
|
||||
internal_format, image_size, buffer);
|
||||
internal_format, image_size, mip_offset);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
@@ -306,7 +316,7 @@ void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& stagin
|
||||
static_cast<GLsizei>(params.GetMipWidth(level)),
|
||||
static_cast<GLsizei>(params.GetMipHeight(level)),
|
||||
static_cast<GLsizei>(params.GetMipDepth(level)),
|
||||
internal_format, image_size, buffer);
|
||||
internal_format, image_size, mip_offset);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap: {
|
||||
const std::size_t layer_size{params.GetHostLayerSize(level)};
|
||||
@@ -315,8 +325,8 @@ void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& stagin
|
||||
static_cast<GLsizei>(params.GetMipWidth(level)),
|
||||
static_cast<GLsizei>(params.GetMipHeight(level)), 1,
|
||||
internal_format, static_cast<GLsizei>(layer_size),
|
||||
buffer);
|
||||
buffer += layer_size;
|
||||
mip_offset);
|
||||
mip_offset += layer_size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -327,17 +337,17 @@ void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& stagin
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
glTextureSubImage1D(texture.handle, level, 0, params.GetMipWidth(level), format, type,
|
||||
buffer);
|
||||
mip_offset);
|
||||
break;
|
||||
case SurfaceTarget::TextureBuffer:
|
||||
ASSERT(level == 0);
|
||||
glNamedBufferSubData(texture_buffer.handle, 0,
|
||||
params.GetMipWidth(level) * params.GetBytesPerPixel(), buffer);
|
||||
params.GetMipWidth(level) * params.GetBytesPerPixel(), mip_offset);
|
||||
break;
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2D:
|
||||
glTextureSubImage2D(texture.handle, level, 0, 0, params.GetMipWidth(level),
|
||||
params.GetMipHeight(level), format, type, buffer);
|
||||
params.GetMipHeight(level), format, type, mip_offset);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
@@ -345,16 +355,18 @@ void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& stagin
|
||||
glTextureSubImage3D(
|
||||
texture.handle, level, 0, 0, 0, static_cast<GLsizei>(params.GetMipWidth(level)),
|
||||
static_cast<GLsizei>(params.GetMipHeight(level)),
|
||||
static_cast<GLsizei>(params.GetMipDepth(level)), format, type, buffer);
|
||||
static_cast<GLsizei>(params.GetMipDepth(level)), format, type, mip_offset);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::TextureCubemap: {
|
||||
const std::size_t layer_size = params.GetHostLayerSize(level);
|
||||
for (std::size_t face = 0; face < params.depth; ++face) {
|
||||
glTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face),
|
||||
params.GetMipWidth(level), params.GetMipHeight(level), 1,
|
||||
format, type, buffer);
|
||||
buffer += params.GetHostLayerSize(level);
|
||||
format, type, mip_offset);
|
||||
mip_offset += layer_size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -452,7 +464,7 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
||||
TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
|
||||
VideoCore::RasterizerInterface& rasterizer,
|
||||
const Device& device)
|
||||
: TextureCacheBase{system, rasterizer} {
|
||||
: TextureCacheBase{system, rasterizer, std::make_unique<StagingBufferCache>(device)} {
|
||||
src_framebuffer.Create();
|
||||
dst_framebuffer.Create();
|
||||
}
|
||||
@@ -460,7 +472,7 @@ TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
|
||||
TextureCacheOpenGL::~TextureCacheOpenGL() = default;
|
||||
|
||||
Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
|
||||
return std::make_shared<CachedSurface>(gpu_addr, params);
|
||||
return std::make_shared<CachedSurface>(gpu_addr, params, temporary_buffer);
|
||||
}
|
||||
|
||||
void TextureCacheOpenGL::ImageCopy(Surface& src_surface, Surface& dst_surface,
|
||||
@@ -568,7 +580,6 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface)
|
||||
glGetTextureImage(src_surface->GetTexture(), 0, source_format.format, source_format.type,
|
||||
static_cast<GLsizei>(source_size), nullptr);
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo_handle);
|
||||
|
||||
@@ -604,7 +615,6 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface)
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
glTextureBarrier();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_staging_buffer.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@@ -26,21 +27,23 @@ using VideoCommon::ViewParams;
|
||||
|
||||
class CachedSurfaceView;
|
||||
class CachedSurface;
|
||||
class StagingBuffer;
|
||||
class TextureCacheOpenGL;
|
||||
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
using View = std::shared_ptr<CachedSurfaceView>;
|
||||
using TextureCacheBase = VideoCommon::TextureCache<Surface, View>;
|
||||
using TextureCacheBase = VideoCommon::TextureCache<Surface, View, StagingBuffer>;
|
||||
|
||||
class CachedSurface final : public VideoCommon::SurfaceBase<View> {
|
||||
class CachedSurface final : public VideoCommon::SurfaceBase<View, StagingBuffer> {
|
||||
friend CachedSurfaceView;
|
||||
|
||||
public:
|
||||
explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params);
|
||||
explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
std::vector<u8>& temporary_buffer);
|
||||
~CachedSurface();
|
||||
|
||||
void UploadTexture(const std::vector<u8>& staging_buffer) override;
|
||||
void DownloadTexture(std::vector<u8>& staging_buffer) override;
|
||||
void UploadTexture(StagingBuffer& buffer) override;
|
||||
void DownloadTexture(StagingBuffer& buffer) override;
|
||||
|
||||
GLenum GetTarget() const {
|
||||
return target;
|
||||
@@ -57,7 +60,7 @@ protected:
|
||||
View CreateViewInner(const ViewParams& view_key, bool is_proxy);
|
||||
|
||||
private:
|
||||
void UploadTextureMipmap(u32 level, const std::vector<u8>& staging_buffer);
|
||||
void UploadTextureMipmap(u32 level, const u8* opengl_pointer);
|
||||
|
||||
GLenum internal_format{};
|
||||
GLenum format{};
|
||||
@@ -138,6 +141,7 @@ private:
|
||||
OGLFramebuffer src_framebuffer;
|
||||
OGLFramebuffer dst_framebuffer;
|
||||
std::unordered_map<u32, OGLBuffer> copy_pbo_cache;
|
||||
std::vector<u8> temporary_buffer;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -101,9 +101,7 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::Syst
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
void RendererOpenGL::SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
|
||||
|
||||
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
|
||||
// Maintain the rasterizer's state as a priority
|
||||
@@ -113,9 +111,9 @@ void RendererOpenGL::SwapBuffers(
|
||||
|
||||
if (framebuffer) {
|
||||
// If framebuffer is provided, reload it from memory to a texture
|
||||
if (screen_info.texture.width != (GLsizei)framebuffer->get().width ||
|
||||
screen_info.texture.height != (GLsizei)framebuffer->get().height ||
|
||||
screen_info.texture.pixel_format != framebuffer->get().pixel_format) {
|
||||
if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
|
||||
screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
|
||||
screen_info.texture.pixel_format != framebuffer->pixel_format) {
|
||||
// Reallocate texture if the framebuffer size has changed.
|
||||
// This is expected to not happen very often and hence should not be a
|
||||
// performance problem.
|
||||
@@ -149,43 +147,44 @@ void RendererOpenGL::SwapBuffers(
|
||||
* Loads framebuffer from emulated memory into the active OpenGL texture.
|
||||
*/
|
||||
void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
|
||||
const u32 bytes_per_pixel{Tegra::FramebufferConfig::BytesPerPixel(framebuffer.pixel_format)};
|
||||
const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
|
||||
const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
|
||||
|
||||
// Framebuffer orientation handling
|
||||
framebuffer_transform_flags = framebuffer.transform_flags;
|
||||
framebuffer_crop_rect = framebuffer.crop_rect;
|
||||
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
|
||||
// only allows rows to have a memory alignement of 4.
|
||||
ASSERT(framebuffer.stride % 4 == 0);
|
||||
|
||||
if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride)) {
|
||||
// Reset the screen info's display texture to its own permanent texture
|
||||
screen_info.display_texture = screen_info.texture.resource.handle;
|
||||
|
||||
rasterizer->FlushRegion(ToCacheAddr(Memory::GetPointer(framebuffer_addr)), size_in_bytes);
|
||||
|
||||
constexpr u32 linear_bpp = 4;
|
||||
VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear,
|
||||
framebuffer.width, framebuffer.height, bytes_per_pixel,
|
||||
linear_bpp, Memory::GetPointer(framebuffer_addr),
|
||||
gl_framebuffer_data.data());
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
|
||||
|
||||
// Update existing texture
|
||||
// TODO: Test what happens on hardware when you change the framebuffer dimensions so that
|
||||
// they differ from the LCD resolution.
|
||||
// TODO: Applications could theoretically crash yuzu here by specifying too large
|
||||
// framebuffer sizes. We should make sure that this cannot happen.
|
||||
glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width,
|
||||
framebuffer.height, screen_info.texture.gl_format,
|
||||
screen_info.texture.gl_type, gl_framebuffer_data.data());
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
|
||||
if (rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the screen info's display texture to its own permanent texture
|
||||
screen_info.display_texture = screen_info.texture.resource.handle;
|
||||
|
||||
const auto pixel_format{
|
||||
VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
|
||||
const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
|
||||
const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
|
||||
const auto host_ptr{Memory::GetPointer(framebuffer_addr)};
|
||||
rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes);
|
||||
|
||||
// TODO(Rodrigo): Read this from HLE
|
||||
constexpr u32 block_height_log2 = 4;
|
||||
VideoCore::MortonSwizzle(VideoCore::MortonSwizzleMode::MortonToLinear, pixel_format,
|
||||
framebuffer.stride, block_height_log2, framebuffer.height, 0, 1, 1,
|
||||
gl_framebuffer_data.data(), host_ptr);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
|
||||
|
||||
// Update existing texture
|
||||
// TODO: Test what happens on hardware when you change the framebuffer dimensions so that
|
||||
// they differ from the LCD resolution.
|
||||
// TODO: Applications could theoretically crash yuzu here by specifying too large
|
||||
// framebuffer sizes. We should make sure that this cannot happen.
|
||||
glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width,
|
||||
framebuffer.height, screen_info.texture.gl_format,
|
||||
screen_info.texture.gl_type, gl_framebuffer_data.data());
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,22 +275,29 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
texture.height = framebuffer.height;
|
||||
texture.pixel_format = framebuffer.pixel_format;
|
||||
|
||||
const auto pixel_format{
|
||||
VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
|
||||
const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * bytes_per_pixel);
|
||||
|
||||
GLint internal_format;
|
||||
switch (framebuffer.pixel_format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
internal_format = GL_RGBA8;
|
||||
texture.gl_format = GL_RGBA;
|
||||
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * 4);
|
||||
break;
|
||||
case Tegra::FramebufferConfig::PixelFormat::RGB565:
|
||||
internal_format = GL_RGB565;
|
||||
texture.gl_format = GL_RGB;
|
||||
texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
default:
|
||||
internal_format = GL_RGBA8;
|
||||
texture.gl_format = GL_RGBA;
|
||||
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * 4);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer pixel format: {}",
|
||||
static_cast<u32>(framebuffer.pixel_format));
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
|
||||
static_cast<u32>(framebuffer.pixel_format));
|
||||
}
|
||||
|
||||
texture.resource.Release();
|
||||
|
||||
@@ -43,14 +43,13 @@ struct ScreenInfo {
|
||||
TextureInfo texture;
|
||||
};
|
||||
|
||||
class RendererOpenGL : public VideoCore::RendererBase {
|
||||
class RendererOpenGL final : public VideoCore::RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||
|
||||
/// Initialize the renderer
|
||||
bool Init() override;
|
||||
|
||||
@@ -14,6 +14,12 @@ using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::Register;
|
||||
|
||||
namespace {
|
||||
constexpr OperationCode GetFloatSelector(u64 selector) {
|
||||
return selector == 0 ? OperationCode::FCastHalf0 : OperationCode::FCastHalf1;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
@@ -22,7 +28,7 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::I2I_R:
|
||||
case OpCode::Id::I2I_C:
|
||||
case OpCode::Id::I2I_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||
UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0);
|
||||
UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF(instr.alu.saturate_d);
|
||||
|
||||
@@ -57,8 +63,8 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::I2F_R:
|
||||
case OpCode::Id::I2F_C:
|
||||
case OpCode::Id::I2F_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0);
|
||||
UNIMPLEMENTED_IF(instr.conversion.dst_size == Register::Size::Long);
|
||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in I2F is not implemented");
|
||||
|
||||
@@ -113,8 +119,10 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
}();
|
||||
|
||||
if (instr.conversion.src_size == Register::Size::Short) {
|
||||
// TODO: figure where extract is sey in the encoding
|
||||
value = Operation(OperationCode::FCastHalf0, PRECISE, value);
|
||||
value = Operation(GetFloatSelector(instr.conversion.float_src.selector), NO_PRECISE,
|
||||
std::move(value));
|
||||
} else {
|
||||
ASSERT(instr.conversion.float_src.selector == 0);
|
||||
}
|
||||
|
||||
value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a);
|
||||
@@ -169,8 +177,10 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
}();
|
||||
|
||||
if (instr.conversion.src_size == Register::Size::Short) {
|
||||
// TODO: figure where extract is sey in the encoding
|
||||
value = Operation(OperationCode::FCastHalf0, PRECISE, value);
|
||||
value = Operation(GetFloatSelector(instr.conversion.float_src.selector), NO_PRECISE,
|
||||
std::move(value));
|
||||
} else {
|
||||
ASSERT(instr.conversion.float_src.selector == 0);
|
||||
}
|
||||
|
||||
value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a);
|
||||
|
||||
@@ -17,8 +17,8 @@ using Tegra::Shader::Pred;
|
||||
u32 ShaderIR::DecodeFloatSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
|
||||
const Node op_a = GetOperandAbsNegFloat(GetRegister(instr.gpr8), instr.fsetp.abs_a != 0,
|
||||
instr.fsetp.neg_a != 0);
|
||||
Node op_a = GetOperandAbsNegFloat(GetRegister(instr.gpr8), instr.fsetp.abs_a != 0,
|
||||
instr.fsetp.neg_a != 0);
|
||||
Node op_b = [&]() {
|
||||
if (instr.is_b_imm) {
|
||||
return GetImmediate19(instr);
|
||||
@@ -28,12 +28,13 @@ u32 ShaderIR::DecodeFloatSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
|
||||
}
|
||||
}();
|
||||
op_b = GetOperandAbsNegFloat(op_b, instr.fsetp.abs_b, false);
|
||||
op_b = GetOperandAbsNegFloat(std::move(op_b), instr.fsetp.abs_b, instr.fsetp.neg_b);
|
||||
|
||||
// We can't use the constant predicate as destination.
|
||||
ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
|
||||
|
||||
const Node predicate = GetPredicateComparisonFloat(instr.fsetp.cond, op_a, op_b);
|
||||
const Node predicate =
|
||||
GetPredicateComparisonFloat(instr.fsetp.cond, std::move(op_a), std::move(op_b));
|
||||
const Node second_pred = GetPredicate(instr.fsetp.pred39, instr.fsetp.neg_pred != 0);
|
||||
|
||||
const OperationCode combiner = GetPredicateCombiner(instr.fsetp.op);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
template <typename StagingBufferType>
|
||||
class StagingBufferCache {
|
||||
using Cache = std::unordered_map<u32, std::vector<std::unique_ptr<StagingBufferType>>>;
|
||||
|
||||
public:
|
||||
explicit StagingBufferCache(bool can_flush_aot) : can_flush_aot{can_flush_aot} {}
|
||||
virtual ~StagingBufferCache() = default;
|
||||
|
||||
StagingBufferType& GetWriteBuffer(std::size_t size) {
|
||||
return GetBuffer(size, false);
|
||||
}
|
||||
|
||||
StagingBufferType& GetReadBuffer(std::size_t size) {
|
||||
return GetBuffer(size, true);
|
||||
}
|
||||
|
||||
bool CanFlushAheadOfTime() const {
|
||||
return can_flush_aot;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<StagingBufferType> CreateBuffer(std::size_t size, bool is_flush) = 0;
|
||||
|
||||
private:
|
||||
StagingBufferType& GetBuffer(std::size_t size, bool is_flush) {
|
||||
const u32 ceil = Common::Log2Ceil64(size);
|
||||
auto& buffers = (is_flush ? flush_cache : upload_cache)[ceil];
|
||||
const auto it = std::find_if(buffers.begin(), buffers.end(),
|
||||
[](auto& buffer) { return buffer->IsAvailable(); });
|
||||
if (it != buffers.end()) {
|
||||
return **it;
|
||||
}
|
||||
return *buffers.emplace_back(CreateBuffer(1ULL << ceil, is_flush));
|
||||
}
|
||||
|
||||
bool can_flush_aot{};
|
||||
Cache upload_cache;
|
||||
Cache flush_cache;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -445,11 +445,12 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
return PixelFormat::ABGR8U;
|
||||
case Tegra::FramebufferConfig::PixelFormat::RGB565:
|
||||
return PixelFormat::B5G6R5U;
|
||||
case Tegra::FramebufferConfig::PixelFormat::BGRA8:
|
||||
return PixelFormat::BGRA8;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
|
||||
return PixelFormat::ABGR8U;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,10 @@ using Tegra::Texture::ConvertFromGuestToHost;
|
||||
using VideoCore::MortonSwizzleMode;
|
||||
using VideoCore::Surface::SurfaceCompression;
|
||||
|
||||
StagingCache::StagingCache() = default;
|
||||
|
||||
StagingCache::~StagingCache() = default;
|
||||
|
||||
SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: params{params}, host_memory_size{params.GetHostSizeInBytes()}, gpu_addr{gpu_addr},
|
||||
SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
std::vector<u8>& temporary_buffer)
|
||||
: params{params}, temporary_buffer{temporary_buffer},
|
||||
host_memory_size{params.GetHostSizeInBytes()}, gpu_addr{gpu_addr},
|
||||
mipmap_sizes(params.num_levels), mipmap_offsets(params.num_levels) {
|
||||
std::size_t offset = 0;
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
@@ -45,6 +43,8 @@ SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceBaseImpl::~SurfaceBaseImpl() = default;
|
||||
|
||||
MatchTopologyResult SurfaceBaseImpl::MatchesTopology(const SurfaceParams& rhs) const {
|
||||
const u32 src_bpp{params.GetBytesPerPixel()};
|
||||
const u32 dst_bpp{rhs.GetBytesPerPixel()};
|
||||
@@ -179,10 +179,8 @@ void SurfaceBaseImpl::SwizzleFunc(MortonSwizzleMode mode, u8* memory, const Surf
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
StagingCache& staging_cache) {
|
||||
void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager, u8* staging_buffer) {
|
||||
MICROPROFILE_SCOPE(GPU_Load_Texture);
|
||||
auto& staging_buffer = staging_cache.GetBuffer(0);
|
||||
u8* host_ptr;
|
||||
is_continuous = memory_manager.IsBlockContinuous(gpu_addr, guest_memory_size);
|
||||
|
||||
@@ -195,9 +193,8 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
}
|
||||
} else {
|
||||
// Use an extra temporal buffer
|
||||
auto& tmp_buffer = staging_cache.GetBuffer(1);
|
||||
tmp_buffer.resize(guest_memory_size);
|
||||
host_ptr = tmp_buffer.data();
|
||||
temporary_buffer.resize(guest_memory_size);
|
||||
host_ptr = temporary_buffer.data();
|
||||
memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
|
||||
}
|
||||
|
||||
@@ -207,7 +204,7 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)};
|
||||
SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params,
|
||||
staging_buffer.data() + host_offset, level);
|
||||
staging_buffer + host_offset, level);
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(params.num_levels == 1, "Linear mipmap loading is not implemented");
|
||||
@@ -218,10 +215,10 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
const u32 height{(params.height + block_height - 1) / block_height};
|
||||
const u32 copy_size{width * bpp};
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes());
|
||||
std::memcpy(staging_buffer, host_ptr, params.GetHostSizeInBytes());
|
||||
} else {
|
||||
const u8* start{host_ptr};
|
||||
u8* write_to{staging_buffer.data()};
|
||||
u8* write_to{staging_buffer};
|
||||
for (u32 h = height; h > 0; --h) {
|
||||
std::memcpy(write_to, start, copy_size);
|
||||
start += params.pitch;
|
||||
@@ -241,18 +238,16 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
const std::size_t out_host_offset = compression_type == SurfaceCompression::Rearranged
|
||||
? in_host_offset
|
||||
: params.GetConvertedMipmapOffset(level);
|
||||
u8* in_buffer = staging_buffer.data() + in_host_offset;
|
||||
u8* out_buffer = staging_buffer.data() + out_host_offset;
|
||||
u8* in_buffer = staging_buffer + in_host_offset;
|
||||
u8* out_buffer = staging_buffer + out_host_offset;
|
||||
ConvertFromGuestToHost(in_buffer, out_buffer, params.pixel_format,
|
||||
params.GetMipWidth(level), params.GetMipHeight(level),
|
||||
params.GetMipDepth(level), true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||
StagingCache& staging_cache) {
|
||||
void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager, u8* staging_buffer) {
|
||||
MICROPROFILE_SCOPE(GPU_Flush_Texture);
|
||||
auto& staging_buffer = staging_cache.GetBuffer(0);
|
||||
u8* host_ptr;
|
||||
|
||||
// Handle continuouty
|
||||
@@ -264,9 +259,8 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||
}
|
||||
} else {
|
||||
// Use an extra temporal buffer
|
||||
auto& tmp_buffer = staging_cache.GetBuffer(1);
|
||||
tmp_buffer.resize(guest_memory_size);
|
||||
host_ptr = tmp_buffer.data();
|
||||
temporary_buffer.resize(guest_memory_size);
|
||||
host_ptr = temporary_buffer.data();
|
||||
}
|
||||
|
||||
if (params.is_tiled) {
|
||||
@@ -274,7 +268,7 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)};
|
||||
SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params,
|
||||
staging_buffer.data() + host_offset, level);
|
||||
staging_buffer + host_offset, level);
|
||||
}
|
||||
} else {
|
||||
ASSERT(params.target == SurfaceTarget::Texture2D);
|
||||
@@ -283,10 +277,10 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||
const u32 bpp{params.GetBytesPerPixel()};
|
||||
const u32 copy_size{params.width * bpp};
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(host_ptr, staging_buffer.data(), guest_memory_size);
|
||||
std::memcpy(host_ptr, staging_buffer, guest_memory_size);
|
||||
} else {
|
||||
u8* start{host_ptr};
|
||||
const u8* read_to{staging_buffer.data()};
|
||||
const u8* read_to{staging_buffer};
|
||||
for (u32 h = params.height; h > 0; --h) {
|
||||
std::memcpy(start, read_to, copy_size);
|
||||
start += params.pitch;
|
||||
|
||||
@@ -38,32 +38,11 @@ enum class MatchTopologyResult : u32 {
|
||||
None = 2,
|
||||
};
|
||||
|
||||
class StagingCache {
|
||||
public:
|
||||
explicit StagingCache();
|
||||
~StagingCache();
|
||||
|
||||
std::vector<u8>& GetBuffer(std::size_t index) {
|
||||
return staging_buffer[index];
|
||||
}
|
||||
|
||||
const std::vector<u8>& GetBuffer(std::size_t index) const {
|
||||
return staging_buffer[index];
|
||||
}
|
||||
|
||||
void SetSize(std::size_t size) {
|
||||
staging_buffer.resize(size);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::vector<u8>> staging_buffer;
|
||||
};
|
||||
|
||||
class SurfaceBaseImpl {
|
||||
public:
|
||||
void LoadBuffer(Tegra::MemoryManager& memory_manager, StagingCache& staging_cache);
|
||||
void LoadBuffer(Tegra::MemoryManager& memory_manager, u8* staging_buffer);
|
||||
|
||||
void FlushBuffer(Tegra::MemoryManager& memory_manager, StagingCache& staging_cache);
|
||||
void FlushBuffer(Tegra::MemoryManager& memory_manager, u8* staging_buffer);
|
||||
|
||||
GPUVAddr GetGpuAddr() const {
|
||||
return gpu_addr;
|
||||
@@ -161,12 +140,15 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params);
|
||||
~SurfaceBaseImpl() = default;
|
||||
explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
std::vector<u8>& temporary_buffer);
|
||||
~SurfaceBaseImpl();
|
||||
|
||||
virtual void DecorateSurfaceName() = 0;
|
||||
|
||||
const SurfaceParams params;
|
||||
std::vector<u8>& temporary_buffer;
|
||||
|
||||
std::size_t layer_size;
|
||||
std::size_t guest_memory_size;
|
||||
const std::size_t host_memory_size;
|
||||
@@ -188,25 +170,40 @@ private:
|
||||
std::vector<CopyParams> BreakDownNonLayered(const SurfaceParams& in_params) const;
|
||||
};
|
||||
|
||||
template <typename TView>
|
||||
template <typename TView, typename StagingBufferType>
|
||||
class SurfaceBase : public SurfaceBaseImpl {
|
||||
public:
|
||||
virtual void UploadTexture(const std::vector<u8>& staging_buffer) = 0;
|
||||
virtual void UploadTexture(StagingBufferType& buffer) = 0;
|
||||
|
||||
virtual void DownloadTexture(std::vector<u8>& staging_buffer) = 0;
|
||||
virtual void DownloadTexture(StagingBufferType& buffer) = 0;
|
||||
|
||||
void SetFlushBuffer(StagingBufferType* buffer) {
|
||||
flush_buffer = buffer;
|
||||
}
|
||||
|
||||
StagingBufferType* GetFlushBuffer() const {
|
||||
return flush_buffer;
|
||||
}
|
||||
|
||||
void MarkAsModified(const bool is_modified_, const u64 tick) {
|
||||
is_modified = is_modified_ || is_target;
|
||||
modification_tick = tick;
|
||||
|
||||
if (is_modified && flush_buffer) {
|
||||
// The buffer has been modified while we thought it was no longer being to be used and
|
||||
// we queued a flush.
|
||||
flush_buffer->Discard();
|
||||
flush_buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MarkAsRenderTarget(const bool is_target, const u32 index) {
|
||||
this->is_target = is_target;
|
||||
this->index = index;
|
||||
void MarkAsRenderTarget(const bool is_target_, const u32 index_) {
|
||||
is_target = is_target_;
|
||||
index = index_;
|
||||
}
|
||||
|
||||
void MarkAsPicked(const bool is_picked) {
|
||||
this->is_picked = is_picked;
|
||||
void MarkAsPicked(const bool is_picked_) {
|
||||
is_picked = is_picked_;
|
||||
}
|
||||
|
||||
bool IsModified() const {
|
||||
@@ -214,7 +211,7 @@ public:
|
||||
}
|
||||
|
||||
bool IsProtected() const {
|
||||
// Only 3D Slices are to be protected
|
||||
// Only 3D slices are to be protected
|
||||
return is_target && params.block_depth > 0;
|
||||
}
|
||||
|
||||
@@ -292,8 +289,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: SurfaceBaseImpl(gpu_addr, params) {}
|
||||
explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
std::vector<u8>& temporary_buffer)
|
||||
: SurfaceBaseImpl{gpu_addr, params, temporary_buffer} {}
|
||||
|
||||
~SurfaceBase() = default;
|
||||
|
||||
@@ -320,6 +318,8 @@ private:
|
||||
bool is_picked{};
|
||||
u32 index{NO_RT};
|
||||
u64 modification_tick{};
|
||||
|
||||
StagingBufferType* flush_buffer{};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/staging_buffer_cache.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/copy_params.h"
|
||||
#include "video_core/texture_cache/surface_base.h"
|
||||
@@ -48,7 +49,7 @@ using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig;
|
||||
|
||||
template <typename TSurface, typename TView>
|
||||
template <typename TSurface, typename TView, typename StagingBufferType>
|
||||
class TextureCache {
|
||||
using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface>>;
|
||||
using IntervalType = typename IntervalMap::interval_type;
|
||||
@@ -62,10 +63,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
/**
|
||||
* `Guard` guarantees that rendertargets don't unregister themselves if the
|
||||
* collide. Protection is currently only done on 3D slices.
|
||||
***/
|
||||
*/
|
||||
void GuardRenderTargets(bool new_guard) {
|
||||
guard_render_targets = new_guard;
|
||||
}
|
||||
@@ -132,12 +133,18 @@ public:
|
||||
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
||||
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
||||
auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents, true);
|
||||
if (depth_buffer.target)
|
||||
if (auto& old_target = depth_buffer.target; old_target != surface_view.first) {
|
||||
FlushAoT(old_target);
|
||||
}
|
||||
|
||||
if (depth_buffer.target) {
|
||||
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
|
||||
}
|
||||
depth_buffer.target = surface_view.first;
|
||||
depth_buffer.view = surface_view.second;
|
||||
if (depth_buffer.target)
|
||||
if (depth_buffer.target) {
|
||||
depth_buffer.target->MarkAsRenderTarget(true, DEPTH_RT);
|
||||
}
|
||||
return surface_view.second;
|
||||
}
|
||||
|
||||
@@ -166,12 +173,18 @@ public:
|
||||
|
||||
auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||
preserve_contents, true);
|
||||
if (render_targets[index].target)
|
||||
if (auto& old_target = render_targets[index].target; old_target != surface_view.first) {
|
||||
FlushAoT(old_target);
|
||||
}
|
||||
|
||||
if (render_targets[index].target) {
|
||||
render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
|
||||
}
|
||||
render_targets[index].target = surface_view.first;
|
||||
render_targets[index].view = surface_view.second;
|
||||
if (render_targets[index].target)
|
||||
if (render_targets[index].target) {
|
||||
render_targets[index].target->MarkAsRenderTarget(true, static_cast<u32>(index));
|
||||
}
|
||||
return surface_view.second;
|
||||
}
|
||||
|
||||
@@ -188,19 +201,25 @@ public:
|
||||
}
|
||||
|
||||
void SetEmptyDepthBuffer() {
|
||||
if (depth_buffer.target == nullptr) {
|
||||
auto& target = depth_buffer.target;
|
||||
if (target == nullptr) {
|
||||
return;
|
||||
}
|
||||
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
|
||||
FlushAoT(target);
|
||||
target->MarkAsRenderTarget(false, NO_RT);
|
||||
|
||||
depth_buffer.target = nullptr;
|
||||
depth_buffer.view = nullptr;
|
||||
}
|
||||
|
||||
void SetEmptyColorBuffer(std::size_t index) {
|
||||
if (render_targets[index].target == nullptr) {
|
||||
auto& target = render_targets[index].target;
|
||||
if (target == nullptr) {
|
||||
return;
|
||||
}
|
||||
render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
|
||||
FlushAoT(target);
|
||||
target->MarkAsRenderTarget(false, NO_RT);
|
||||
|
||||
render_targets[index].target = nullptr;
|
||||
render_targets[index].view = nullptr;
|
||||
}
|
||||
@@ -235,14 +254,15 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: system{system}, rasterizer{rasterizer} {
|
||||
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||
std::unique_ptr<StagingBufferCache<StagingBufferType>> staging_buffer_cache)
|
||||
: system{system}, rasterizer{rasterizer}, staging_buffer_cache{
|
||||
std::move(staging_buffer_cache)} {
|
||||
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
SetEmptyColorBuffer(i);
|
||||
}
|
||||
|
||||
SetEmptyDepthBuffer();
|
||||
staging_cache.SetSize(2);
|
||||
|
||||
const auto make_siblings = [this](PixelFormat a, PixelFormat b) {
|
||||
siblings_table[static_cast<std::size_t>(a)] = b;
|
||||
@@ -687,9 +707,13 @@ private:
|
||||
}
|
||||
|
||||
void LoadSurface(const TSurface& surface) {
|
||||
staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes());
|
||||
surface->LoadBuffer(system.GPU().MemoryManager(), staging_cache);
|
||||
surface->UploadTexture(staging_cache.GetBuffer(0));
|
||||
const auto host_size = surface->GetHostSizeInBytes();
|
||||
auto& buffer = staging_buffer_cache->GetWriteBuffer(host_size);
|
||||
|
||||
surface->LoadBuffer(system.GPU().MemoryManager(), buffer.Map(host_size));
|
||||
buffer.Unmap(host_size);
|
||||
|
||||
surface->UploadTexture(buffer);
|
||||
surface->MarkAsModified(false, Tick());
|
||||
}
|
||||
|
||||
@@ -697,9 +721,18 @@ private:
|
||||
if (!surface->IsModified()) {
|
||||
return;
|
||||
}
|
||||
staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes());
|
||||
surface->DownloadTexture(staging_cache.GetBuffer(0));
|
||||
surface->FlushBuffer(system.GPU().MemoryManager(), staging_cache);
|
||||
|
||||
const auto host_size = surface->GetHostSizeInBytes();
|
||||
auto buffer = surface->GetFlushBuffer();
|
||||
if (!buffer) {
|
||||
buffer = &staging_buffer_cache->GetReadBuffer(host_size);
|
||||
surface->DownloadTexture(*buffer);
|
||||
}
|
||||
buffer->WaitFence();
|
||||
surface->SetFlushBuffer(nullptr);
|
||||
|
||||
surface->FlushBuffer(system.GPU().MemoryManager(), buffer->Map(host_size));
|
||||
buffer->Unmap(host_size);
|
||||
surface->MarkAsModified(false, Tick());
|
||||
}
|
||||
|
||||
@@ -767,6 +800,16 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
void FlushAoT(TSurface& surface) {
|
||||
if (staging_buffer_cache->CanFlushAheadOfTime() || !surface || !surface->IsLinear() ||
|
||||
surface->GetFlushBuffer()) {
|
||||
return;
|
||||
}
|
||||
auto& buffer = staging_buffer_cache->GetReadBuffer(surface->GetHostSizeInBytes());
|
||||
surface->DownloadTexture(buffer);
|
||||
surface->SetFlushBuffer(&buffer);
|
||||
}
|
||||
|
||||
constexpr PixelFormat GetSiblingFormat(PixelFormat format) const {
|
||||
return siblings_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
@@ -813,7 +856,7 @@ private:
|
||||
|
||||
std::vector<TSurface> sampled_textures;
|
||||
|
||||
StagingCache staging_cache;
|
||||
std::unique_ptr<StagingBufferCache<StagingBufferType>> staging_buffer_cache;
|
||||
std::recursive_mutex mutex;
|
||||
};
|
||||
|
||||
|
||||
@@ -516,6 +516,7 @@ void Config::ReadPathValues() {
|
||||
|
||||
UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
|
||||
UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
|
||||
UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString();
|
||||
UISettings::values.game_directory_path =
|
||||
ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
|
||||
UISettings::values.game_directory_deepscan =
|
||||
|
||||
@@ -119,6 +119,7 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
extern "C" {
|
||||
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
||||
// graphics
|
||||
@@ -747,6 +748,18 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::PreventOSSleep() {
|
||||
#ifdef _WIN32
|
||||
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GMainWindow::AllowOSSleep() {
|
||||
#ifdef _WIN32
|
||||
SetThreadExecutionState(ES_CONTINUOUS);
|
||||
#endif
|
||||
}
|
||||
|
||||
QStringList GMainWindow::GetUnsupportedGLExtensions() {
|
||||
QStringList unsupported_ext;
|
||||
|
||||
@@ -966,6 +979,8 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
}
|
||||
|
||||
void GMainWindow::ShutdownGame() {
|
||||
AllowOSSleep();
|
||||
|
||||
discord_rpc->Pause();
|
||||
emu_thread->RequestStop();
|
||||
|
||||
@@ -1567,6 +1582,8 @@ void GMainWindow::OnMenuRecentFile() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnStartGame() {
|
||||
PreventOSSleep();
|
||||
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
|
||||
@@ -1598,6 +1615,8 @@ void GMainWindow::OnPauseGame() {
|
||||
ui.action_Pause->setEnabled(false);
|
||||
ui.action_Stop->setEnabled(true);
|
||||
ui.action_Capture_Screenshot->setEnabled(false);
|
||||
|
||||
AllowOSSleep();
|
||||
}
|
||||
|
||||
void GMainWindow::OnStopGame() {
|
||||
|
||||
@@ -130,6 +130,9 @@ private:
|
||||
void ConnectWidgetEvents();
|
||||
void ConnectMenuEvents();
|
||||
|
||||
void PreventOSSleep();
|
||||
void AllowOSSleep();
|
||||
|
||||
QStringList GetUnsupportedGLExtensions();
|
||||
bool LoadROM(const QString& filename);
|
||||
void BootGame(const QString& filename);
|
||||
|
||||
Reference in New Issue
Block a user