GL renderer
This commit is contained in:
parent
a43b6ee9db
commit
2792bf0239
|
@ -56,19 +56,17 @@
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\gl\context_wgl.cpp" />
|
<ClCompile Include="gpu\gl\context_wgl.cpp" />
|
||||||
<ClCompile Include="gpu\gl\program.cpp" />
|
|
||||||
<ClCompile Include="gpu\gl\shader_cache.cpp" />
|
|
||||||
<ClCompile Include="gpu\gl\stream_buffer.cpp" />
|
|
||||||
<ClCompile Include="gpu\gl\texture.cpp" />
|
|
||||||
<ClCompile Include="gpu\gl\x11_window.cpp">
|
<ClCompile Include="gpu\gl\x11_window.cpp">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\gpu_device.cpp" />
|
<ClCompile Include="gpu\gpu_device.cpp" />
|
||||||
<ClCompile Include="gpu\gpu_texture.cpp" />
|
<ClCompile Include="gpu\gpu_texture.cpp" />
|
||||||
<ClCompile Include="gpu\imgui_impl_dx12.cpp" />
|
<ClCompile Include="gpu\imgui_impl_dx12.cpp" />
|
||||||
<ClCompile Include="gpu\imgui_impl_opengl3.cpp" />
|
|
||||||
<ClCompile Include="gpu\imgui_impl_vulkan.cpp" />
|
<ClCompile Include="gpu\imgui_impl_vulkan.cpp" />
|
||||||
<ClCompile Include="gpu\opengl_gpu_device.cpp" />
|
<ClCompile Include="gpu\opengl_device.cpp" />
|
||||||
|
<ClCompile Include="gpu\opengl_pipeline.cpp" />
|
||||||
|
<ClCompile Include="gpu\opengl_stream_buffer.cpp" />
|
||||||
|
<ClCompile Include="gpu\opengl_texture.cpp" />
|
||||||
<ClCompile Include="gpu\postprocessing_chain.cpp" />
|
<ClCompile Include="gpu\postprocessing_chain.cpp" />
|
||||||
<ClCompile Include="gpu\postprocessing_shader.cpp" />
|
<ClCompile Include="gpu\postprocessing_shader.cpp" />
|
||||||
<ClCompile Include="gpu\postprocessing_shadergen.cpp" />
|
<ClCompile Include="gpu\postprocessing_shadergen.cpp" />
|
||||||
|
@ -97,9 +95,6 @@
|
||||||
<ClCompile Include="gdb_protocol.cpp" />
|
<ClCompile Include="gdb_protocol.cpp" />
|
||||||
<ClCompile Include="gpu.cpp" />
|
<ClCompile Include="gpu.cpp" />
|
||||||
<ClCompile Include="gpu_hw.cpp" />
|
<ClCompile Include="gpu_hw.cpp" />
|
||||||
<ClCompile Include="gpu_hw_opengl.cpp">
|
|
||||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="host.cpp" />
|
<ClCompile Include="host.cpp" />
|
||||||
<ClCompile Include="host_interface_progress_callback.cpp" />
|
<ClCompile Include="host_interface_progress_callback.cpp" />
|
||||||
<ClCompile Include="interrupt_controller.cpp" />
|
<ClCompile Include="interrupt_controller.cpp" />
|
||||||
|
@ -154,13 +149,13 @@
|
||||||
<ClInclude Include="digital_controller.h" />
|
<ClInclude Include="digital_controller.h" />
|
||||||
<ClInclude Include="game_database.h" />
|
<ClInclude Include="game_database.h" />
|
||||||
<ClInclude Include="gpu\d3d11_device.h" />
|
<ClInclude Include="gpu\d3d11_device.h" />
|
||||||
|
<ClInclude Include="gpu\d3d12\texture.h" />
|
||||||
<ClInclude Include="gpu\d3d_shaders.h" />
|
<ClInclude Include="gpu\d3d_shaders.h" />
|
||||||
<ClInclude Include="gpu\d3d12\context.h" />
|
<ClInclude Include="gpu\d3d12\context.h" />
|
||||||
<ClInclude Include="gpu\d3d12\descriptor_heap_manager.h" />
|
<ClInclude Include="gpu\d3d12\descriptor_heap_manager.h" />
|
||||||
<ClInclude Include="gpu\d3d12\shader_cache.h" />
|
<ClInclude Include="gpu\d3d12\shader_cache.h" />
|
||||||
<ClInclude Include="gpu\d3d12\staging_texture.h" />
|
<ClInclude Include="gpu\d3d12\staging_texture.h" />
|
||||||
<ClInclude Include="gpu\d3d12\stream_buffer.h" />
|
<ClInclude Include="gpu\d3d12\stream_buffer.h" />
|
||||||
<ClInclude Include="gpu\d3d12\texture.h" />
|
|
||||||
<ClInclude Include="gpu\d3d12\util.h" />
|
<ClInclude Include="gpu\d3d12\util.h" />
|
||||||
<ClInclude Include="gpu\d3d12_gpu_device.h" />
|
<ClInclude Include="gpu\d3d12_gpu_device.h" />
|
||||||
<ClInclude Include="gpu\gl\context.h" />
|
<ClInclude Include="gpu\gl\context.h" />
|
||||||
|
@ -180,20 +175,18 @@
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\gl\context_wgl.h" />
|
<ClInclude Include="gpu\gl\context_wgl.h" />
|
||||||
<ClInclude Include="gpu\gl\loader.h" />
|
|
||||||
<ClInclude Include="gpu\gl\program.h" />
|
|
||||||
<ClInclude Include="gpu\gl\shader_cache.h" />
|
|
||||||
<ClInclude Include="gpu\gl\stream_buffer.h" />
|
|
||||||
<ClInclude Include="gpu\gl\texture.h" />
|
|
||||||
<ClInclude Include="gpu\gl\x11_window.h">
|
<ClInclude Include="gpu\gl\x11_window.h">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\gpu_device.h" />
|
<ClInclude Include="gpu\gpu_device.h" />
|
||||||
<ClInclude Include="gpu\gpu_texture.h" />
|
<ClInclude Include="gpu\gpu_texture.h" />
|
||||||
<ClInclude Include="gpu\imgui_impl_dx12.h" />
|
<ClInclude Include="gpu\imgui_impl_dx12.h" />
|
||||||
<ClInclude Include="gpu\imgui_impl_opengl3.h" />
|
|
||||||
<ClInclude Include="gpu\imgui_impl_vulkan.h" />
|
<ClInclude Include="gpu\imgui_impl_vulkan.h" />
|
||||||
<ClInclude Include="gpu\opengl_gpu_device.h" />
|
<ClInclude Include="gpu\opengl_device.h" />
|
||||||
|
<ClInclude Include="gpu\opengl_loader.h" />
|
||||||
|
<ClInclude Include="gpu\opengl_pipeline.h" />
|
||||||
|
<ClInclude Include="gpu\opengl_stream_buffer.h" />
|
||||||
|
<ClInclude Include="gpu\opengl_texture.h" />
|
||||||
<ClInclude Include="gpu\postprocessing_chain.h" />
|
<ClInclude Include="gpu\postprocessing_chain.h" />
|
||||||
<ClInclude Include="gpu\postprocessing_shader.h" />
|
<ClInclude Include="gpu\postprocessing_shader.h" />
|
||||||
<ClInclude Include="gpu\postprocessing_shadergen.h" />
|
<ClInclude Include="gpu\postprocessing_shadergen.h" />
|
||||||
|
@ -224,9 +217,6 @@
|
||||||
<ClInclude Include="gdb_protocol.h" />
|
<ClInclude Include="gdb_protocol.h" />
|
||||||
<ClInclude Include="gpu.h" />
|
<ClInclude Include="gpu.h" />
|
||||||
<ClInclude Include="gpu_hw.h" />
|
<ClInclude Include="gpu_hw.h" />
|
||||||
<ClInclude Include="gpu_hw_opengl.h">
|
|
||||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gte_types.h" />
|
<ClInclude Include="gte_types.h" />
|
||||||
<ClInclude Include="host.h" />
|
<ClInclude Include="host.h" />
|
||||||
<ClInclude Include="host_interface_progress_callback.h" />
|
<ClInclude Include="host_interface_progress_callback.h" />
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
<ClCompile Include="dma.cpp" />
|
<ClCompile Include="dma.cpp" />
|
||||||
<ClCompile Include="gdb_protocol.cpp" />
|
<ClCompile Include="gdb_protocol.cpp" />
|
||||||
<ClCompile Include="gpu.cpp" />
|
<ClCompile Include="gpu.cpp" />
|
||||||
<ClCompile Include="gpu_hw_opengl.cpp" />
|
|
||||||
<ClCompile Include="gpu_hw.cpp" />
|
<ClCompile Include="gpu_hw.cpp" />
|
||||||
<ClCompile Include="interrupt_controller.cpp" />
|
<ClCompile Include="interrupt_controller.cpp" />
|
||||||
<ClCompile Include="cdrom.cpp" />
|
<ClCompile Include="cdrom.cpp" />
|
||||||
|
@ -67,7 +66,7 @@
|
||||||
<ClCompile Include="gpu\gpu_device.cpp">
|
<ClCompile Include="gpu\gpu_device.cpp">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\opengl_gpu_device.cpp">
|
<ClCompile Include="gpu\opengl_device.cpp">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\vulkan_gpu_device.cpp">
|
<ClCompile Include="gpu\vulkan_gpu_device.cpp">
|
||||||
|
@ -82,9 +81,6 @@
|
||||||
<ClCompile Include="gpu\d3d12\stream_buffer.cpp">
|
<ClCompile Include="gpu\d3d12\stream_buffer.cpp">
|
||||||
<Filter>gpu\d3d12</Filter>
|
<Filter>gpu\d3d12</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\d3d12\texture.cpp">
|
|
||||||
<Filter>gpu\d3d12</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\d3d12\util.cpp">
|
<ClCompile Include="gpu\d3d12\util.cpp">
|
||||||
<Filter>gpu\d3d12</Filter>
|
<Filter>gpu\d3d12</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -106,18 +102,6 @@
|
||||||
<ClCompile Include="gpu\gl\context_wgl.cpp">
|
<ClCompile Include="gpu\gl\context_wgl.cpp">
|
||||||
<Filter>gpu\gl</Filter>
|
<Filter>gpu\gl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\gl\program.cpp">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\gl\shader_cache.cpp">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\gl\stream_buffer.cpp">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\gl\texture.cpp">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\gl\x11_window.cpp">
|
<ClCompile Include="gpu\gl\x11_window.cpp">
|
||||||
<Filter>gpu\gl</Filter>
|
<Filter>gpu\gl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -133,9 +117,6 @@
|
||||||
<ClCompile Include="gpu\vulkan\swap_chain.cpp">
|
<ClCompile Include="gpu\vulkan\swap_chain.cpp">
|
||||||
<Filter>gpu\vulkan</Filter>
|
<Filter>gpu\vulkan</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\vulkan\texture.cpp">
|
|
||||||
<Filter>gpu\vulkan</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\vulkan\util.cpp">
|
<ClCompile Include="gpu\vulkan\util.cpp">
|
||||||
<Filter>gpu\vulkan</Filter>
|
<Filter>gpu\vulkan</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -166,9 +147,6 @@
|
||||||
<ClCompile Include="gpu\imgui_impl_dx12.cpp">
|
<ClCompile Include="gpu\imgui_impl_dx12.cpp">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gpu\imgui_impl_opengl3.cpp">
|
|
||||||
<Filter>gpu</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="gpu\imgui_impl_vulkan.cpp">
|
<ClCompile Include="gpu\imgui_impl_vulkan.cpp">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -178,6 +156,21 @@
|
||||||
<ClCompile Include="gpu\gpu_shader_cache.cpp">
|
<ClCompile Include="gpu\gpu_shader_cache.cpp">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="gpu\opengl_texture.cpp">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="gpu\opengl_stream_buffer.cpp">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="gpu\opengl_pipeline.cpp">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="gpu\d3d12\texture.cpp">
|
||||||
|
<Filter>gpu\d3d12</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="gpu\vulkan\texture.cpp">
|
||||||
|
<Filter>gpu\vulkan</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -189,7 +182,6 @@
|
||||||
<ClInclude Include="bus.h" />
|
<ClInclude Include="bus.h" />
|
||||||
<ClInclude Include="dma.h" />
|
<ClInclude Include="dma.h" />
|
||||||
<ClInclude Include="gpu.h" />
|
<ClInclude Include="gpu.h" />
|
||||||
<ClInclude Include="gpu_hw_opengl.h" />
|
|
||||||
<ClInclude Include="gpu_hw.h" />
|
<ClInclude Include="gpu_hw.h" />
|
||||||
<ClInclude Include="interrupt_controller.h" />
|
<ClInclude Include="interrupt_controller.h" />
|
||||||
<ClInclude Include="cdrom.h" />
|
<ClInclude Include="cdrom.h" />
|
||||||
|
@ -252,7 +244,7 @@
|
||||||
<ClInclude Include="gpu\gpu_device.h">
|
<ClInclude Include="gpu\gpu_device.h">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\opengl_gpu_device.h">
|
<ClInclude Include="gpu\opengl_device.h">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\vulkan_gpu_device.h">
|
<ClInclude Include="gpu\vulkan_gpu_device.h">
|
||||||
|
@ -267,9 +259,6 @@
|
||||||
<ClInclude Include="gpu\d3d12\stream_buffer.h">
|
<ClInclude Include="gpu\d3d12\stream_buffer.h">
|
||||||
<Filter>gpu\d3d12</Filter>
|
<Filter>gpu\d3d12</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\d3d12\texture.h">
|
|
||||||
<Filter>gpu\d3d12</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\d3d12\util.h">
|
<ClInclude Include="gpu\d3d12\util.h">
|
||||||
<Filter>gpu\d3d12</Filter>
|
<Filter>gpu\d3d12</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -291,21 +280,6 @@
|
||||||
<ClInclude Include="gpu\gl\context_wgl.h">
|
<ClInclude Include="gpu\gl\context_wgl.h">
|
||||||
<Filter>gpu\gl</Filter>
|
<Filter>gpu\gl</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\gl\loader.h">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\gl\program.h">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\gl\shader_cache.h">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\gl\stream_buffer.h">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\gl\texture.h">
|
|
||||||
<Filter>gpu\gl</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\gl\x11_window.h">
|
<ClInclude Include="gpu\gl\x11_window.h">
|
||||||
<Filter>gpu\gl</Filter>
|
<Filter>gpu\gl</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -324,9 +298,6 @@
|
||||||
<ClInclude Include="gpu\vulkan\swap_chain.h">
|
<ClInclude Include="gpu\vulkan\swap_chain.h">
|
||||||
<Filter>gpu\vulkan</Filter>
|
<Filter>gpu\vulkan</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\vulkan\texture.h">
|
|
||||||
<Filter>gpu\vulkan</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\vulkan\util.h">
|
<ClInclude Include="gpu\vulkan\util.h">
|
||||||
<Filter>gpu\vulkan</Filter>
|
<Filter>gpu\vulkan</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -360,9 +331,6 @@
|
||||||
<ClInclude Include="gpu\imgui_impl_dx12.h">
|
<ClInclude Include="gpu\imgui_impl_dx12.h">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gpu\imgui_impl_opengl3.h">
|
|
||||||
<Filter>gpu</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="gpu\imgui_impl_vulkan.h">
|
<ClInclude Include="gpu\imgui_impl_vulkan.h">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -375,6 +343,24 @@
|
||||||
<ClInclude Include="gpu\gpu_shader_cache.h">
|
<ClInclude Include="gpu\gpu_shader_cache.h">
|
||||||
<Filter>gpu</Filter>
|
<Filter>gpu</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="gpu\opengl_texture.h">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="gpu\opengl_stream_buffer.h">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="gpu\opengl_loader.h">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="gpu\opengl_pipeline.h">
|
||||||
|
<Filter>gpu</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="gpu\d3d12\texture.h">
|
||||||
|
<Filter>gpu\d3d12</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="gpu\vulkan\texture.h">
|
||||||
|
<Filter>gpu\vulkan</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="gpu">
|
<Filter Include="gpu">
|
||||||
|
|
|
@ -88,6 +88,7 @@ public:
|
||||||
virtual bool DoState(StateWrapper& sw, GPUTexture** save_to_texture, bool update_display);
|
virtual bool DoState(StateWrapper& sw, GPUTexture** save_to_texture, bool update_display);
|
||||||
|
|
||||||
// Graphics API state reset/restore - call when drawing the UI etc.
|
// Graphics API state reset/restore - call when drawing the UI etc.
|
||||||
|
// TODO: replace with "invalidate cached state"
|
||||||
virtual void RestoreGraphicsAPIState();
|
virtual void RestoreGraphicsAPIState();
|
||||||
|
|
||||||
// Render statistics debug window.
|
// Render statistics debug window.
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include "common/path.h"
|
#include "common/path.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -36,6 +34,7 @@ static unsigned s_next_bad_shader_id = 1;
|
||||||
|
|
||||||
static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view& name)
|
static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view& name)
|
||||||
{
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
// WKPDID_D3DDebugObjectName
|
// WKPDID_D3DDebugObjectName
|
||||||
static constexpr GUID guid = {0x429b8c22, 0x9188, 0x4b0c, 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00};
|
static constexpr GUID guid = {0x429b8c22, 0x9188, 0x4b0c, 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00};
|
||||||
|
|
||||||
|
@ -46,6 +45,7 @@ static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view
|
||||||
|
|
||||||
const std::wstring wname = StringUtil::UTF8StringToWideString(name);
|
const std::wstring wname = StringUtil::UTF8StringToWideString(name);
|
||||||
obj->SetPrivateData(guid, static_cast<UINT>(wname.length()) * 2u, wname.c_str());
|
obj->SetPrivateData(guid, static_cast<UINT>(wname.length()) * 2u, wname.c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D11StreamBuffer::D3D11StreamBuffer() : m_size(0), m_position(0)
|
D3D11StreamBuffer::D3D11StreamBuffer() : m_size(0), m_position(0)
|
||||||
|
@ -419,8 +419,10 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
if (g_settings.gpu_use_debug_device)
|
if (g_settings.gpu_use_debug_device)
|
||||||
m_context.As(&m_annotation);
|
m_context.As(&m_annotation);
|
||||||
|
#endif
|
||||||
|
|
||||||
// we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky.
|
// we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky.
|
||||||
ComPtr<IDXGIDevice> dxgi_device;
|
ComPtr<IDXGIDevice> dxgi_device;
|
||||||
|
@ -600,22 +602,20 @@ bool D3D11Device::CreateSwapChainRTV()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_swap_chain_texture = std::make_unique<D3D11Texture>();
|
D3D11_TEXTURE2D_DESC backbuffer_desc;
|
||||||
if (!m_swap_chain_texture->Adopt(m_device.Get(), std::move(backbuffer)))
|
backbuffer->GetDesc(&backbuffer_desc);
|
||||||
|
|
||||||
|
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
|
||||||
|
backbuffer_desc.ArraySize);
|
||||||
|
hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
m_swap_chain_texture.reset();
|
Log_ErrorPrintf("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(m_swap_chain_framebuffer = std::unique_ptr<D3D11Framebuffer>(
|
m_window_info.surface_width = backbuffer_desc.Width;
|
||||||
static_cast<D3D11Framebuffer*>(CreateFramebuffer(m_swap_chain_texture.get()).release()))))
|
m_window_info.surface_height = backbuffer_desc.Height;
|
||||||
{
|
|
||||||
m_swap_chain_texture.reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_window_info.surface_width = m_swap_chain_texture->GetWidth();
|
|
||||||
m_window_info.surface_height = m_swap_chain_texture->GetHeight();
|
|
||||||
Log_InfoPrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
Log_InfoPrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
|
||||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||||
|
@ -651,8 +651,7 @@ void D3D11Device::DestroySurface()
|
||||||
if (IsFullscreen())
|
if (IsFullscreen())
|
||||||
SetFullscreen(false, 0, 0, 0.0f);
|
SetFullscreen(false, 0, 0, 0.0f);
|
||||||
|
|
||||||
m_swap_chain_framebuffer.reset();
|
m_swap_chain_rtv.Reset();
|
||||||
m_swap_chain_texture.reset();
|
|
||||||
m_swap_chain.Reset();
|
m_swap_chain.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,8 +675,7 @@ void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
||||||
if (!m_swap_chain)
|
if (!m_swap_chain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_swap_chain_framebuffer.reset();
|
m_swap_chain_rtv.Reset();
|
||||||
m_swap_chain_texture.reset();
|
|
||||||
|
|
||||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||||
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||||
|
@ -746,8 +744,7 @@ bool D3D11Device::SetFullscreen(bool fullscreen, u32 width, u32 height, float re
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_swap_chain_framebuffer.reset();
|
m_swap_chain_rtv.Reset();
|
||||||
m_swap_chain_texture.reset();
|
|
||||||
m_swap_chain.Reset();
|
m_swap_chain.Reset();
|
||||||
|
|
||||||
if (!CreateSwapChain(&closest_mode))
|
if (!CreateSwapChain(&closest_mode))
|
||||||
|
@ -782,13 +779,10 @@ void D3D11Device::DestroyBuffers()
|
||||||
m_index_buffer.Release();
|
m_index_buffer.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::Render(bool skip_present)
|
bool D3D11Device::BeginPresent(bool skip_present)
|
||||||
{
|
{
|
||||||
if (skip_present || !m_swap_chain)
|
if (skip_present || !m_swap_chain)
|
||||||
{
|
|
||||||
ImGui::Render();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// When using vsync, the time here seems to include the time for the buffer to become available.
|
// When using vsync, the time here seems to include the time for the buffer to become available.
|
||||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||||
|
@ -797,23 +791,16 @@ bool D3D11Device::Render(bool skip_present)
|
||||||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||||
PopTimestampQuery();
|
PopTimestampQuery();
|
||||||
|
|
||||||
ClearRenderTarget(m_swap_chain_texture.get(), 0);
|
static constexpr float clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color);
|
||||||
|
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
|
||||||
|
m_current_framebuffer = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (HasDisplayTexture())
|
void D3D11Device::EndPresent()
|
||||||
{
|
{
|
||||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
DebugAssert(!m_current_framebuffer);
|
||||||
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", left, top, width, height);
|
|
||||||
RenderDisplay(m_swap_chain_framebuffer.get(), left, top, width, height, m_display_texture, m_display_texture_view_x,
|
|
||||||
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
|
|
||||||
IsUsingLinearFiltering());
|
|
||||||
}
|
|
||||||
|
|
||||||
SetFramebuffer(m_swap_chain_framebuffer.get());
|
|
||||||
SetViewportAndScissor(0, 0, m_swap_chain_framebuffer->GetWidth(), m_swap_chain_framebuffer->GetHeight());
|
|
||||||
|
|
||||||
RenderImGui();
|
|
||||||
|
|
||||||
RenderSoftwareCursor();
|
|
||||||
|
|
||||||
if (!m_vsync_enabled && m_gpu_timing_enabled)
|
if (!m_vsync_enabled && m_gpu_timing_enabled)
|
||||||
PopTimestampQuery();
|
PopTimestampQuery();
|
||||||
|
@ -825,10 +812,6 @@ bool D3D11Device::Render(bool skip_present)
|
||||||
|
|
||||||
if (m_gpu_timing_enabled)
|
if (m_gpu_timing_enabled)
|
||||||
KickTimestampQuery();
|
KickTimestampQuery();
|
||||||
|
|
||||||
m_current_framebuffer = nullptr;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUDevice::AdapterAndModeList D3D11Device::StaticGetAdapterAndModeList()
|
GPUDevice::AdapterAndModeList D3D11Device::StaticGetAdapterAndModeList()
|
||||||
|
@ -1750,8 +1733,11 @@ bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||||
bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
|
bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
|
||||||
u32 level /*= 0*/)
|
u32 level /*= 0*/)
|
||||||
{
|
{
|
||||||
if (!m_dynamic || (x + width) > m_width || (y + height) > m_height || layer > m_layers || level > m_levels)
|
if (!m_dynamic || (x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || layer > m_layers ||
|
||||||
|
level > m_levels)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const bool discard = (width == m_width && height == m_height);
|
const bool discard = (width == m_width && height == m_height);
|
||||||
const u32 srnum = D3D11CalcSubresource(level, layer, m_levels);
|
const u32 srnum = D3D11CalcSubresource(level, layer, m_levels);
|
||||||
|
@ -1780,6 +1766,11 @@ void D3D11Texture::Unmap()
|
||||||
m_mapped_subresource = 0;
|
m_mapped_subresource = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void D3D11Texture::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
SetD3DDebugObjectName(m_texture.Get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
|
bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
|
||||||
Format format, const void* initial_data /* = nullptr */, u32 initial_data_stride /* = 0 */,
|
Format format, const void* initial_data /* = nullptr */, u32 initial_data_stride /* = 0 */,
|
||||||
bool dynamic /* = false */)
|
bool dynamic /* = false */)
|
||||||
|
@ -2016,6 +2007,7 @@ std::unique_ptr<GPUTextureBuffer> D3D11Device::CreateTextureBuffer(GPUTextureBuf
|
||||||
|
|
||||||
void D3D11Device::PushDebugGroup(const char* fmt, ...)
|
void D3D11Device::PushDebugGroup(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2025,18 +2017,22 @@ void D3D11Device::PushDebugGroup(const char* fmt, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
m_annotation->BeginEvent(StringUtil::UTF8StringToWideString(str).c_str());
|
m_annotation->BeginEvent(StringUtil::UTF8StringToWideString(str).c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::PopDebugGroup()
|
void D3D11Device::PopDebugGroup()
|
||||||
{
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_annotation->EndEvent();
|
m_annotation->EndEvent();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::InsertDebugMessage(const char* fmt, ...)
|
void D3D11Device::InsertDebugMessage(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2046,6 +2042,7 @@ void D3D11Device::InsertDebugMessage(const char* fmt, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
m_annotation->SetMarker(StringUtil::UTF8StringToWideString(str).c_str());
|
m_annotation->SetMarker(StringUtil::UTF8StringToWideString(str).c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
|
void D3D11Device::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
|
||||||
|
|
|
@ -221,6 +221,8 @@ public:
|
||||||
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
||||||
void Unmap() override;
|
void Unmap() override;
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ComPtr<ID3D11Texture2D> m_texture;
|
ComPtr<ID3D11Texture2D> m_texture;
|
||||||
ComPtr<ID3D11ShaderResourceView> m_srv;
|
ComPtr<ID3D11ShaderResourceView> m_srv;
|
||||||
|
@ -335,7 +337,8 @@ public:
|
||||||
|
|
||||||
void SetVSync(bool enabled) override;
|
void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
bool Render(bool skip_present) override;
|
bool BeginPresent(bool skip_present) override;
|
||||||
|
void EndPresent() override;
|
||||||
|
|
||||||
void UnbindFramebuffer(D3D11Framebuffer* fb);
|
void UnbindFramebuffer(D3D11Framebuffer* fb);
|
||||||
void UnbindPipeline(D3D11Pipeline* pl);
|
void UnbindPipeline(D3D11Pipeline* pl);
|
||||||
|
@ -387,8 +390,7 @@ private:
|
||||||
|
|
||||||
ComPtr<IDXGIFactory> m_dxgi_factory;
|
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||||
ComPtr<IDXGISwapChain> m_swap_chain;
|
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||||
std::unique_ptr<D3D11Texture> m_swap_chain_texture;
|
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||||
std::unique_ptr<D3D11Framebuffer> m_swap_chain_framebuffer;
|
|
||||||
|
|
||||||
RasterizationStateMap m_rasterization_states;
|
RasterizationStateMap m_rasterization_states;
|
||||||
DepthStateMap m_depth_states;
|
DepthStateMap m_depth_states;
|
||||||
|
|
|
@ -355,6 +355,16 @@ void D3D12::Texture::CopyFromBuffer(u32 x, u32 y, u32 width, u32 height, u32 pit
|
||||||
TransitionToState(old_state);
|
TransitionToState(old_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void D3D12::Texture::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12::Texture::MakeReadyForSampling()
|
||||||
|
{
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D12::Texture::LoadData(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
|
bool D3D12::Texture::LoadData(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
|
||||||
{
|
{
|
||||||
const u32 texel_size = GetPixelSize();
|
const u32 texel_size = GetPixelSize();
|
||||||
|
|
|
@ -63,6 +63,9 @@ public:
|
||||||
static void CopyToUploadBuffer(const void* src_data, u32 src_pitch, u32 height, void* dst_data, u32 dst_pitch);
|
static void CopyToUploadBuffer(const void* src_data, u32 src_pitch, u32 height, void* dst_data, u32 dst_pitch);
|
||||||
void CopyFromBuffer(u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
|
void CopyFromBuffer(u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
void MakeReadyForSampling() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool CreateSRVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled,
|
static bool CreateSRVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled,
|
||||||
DescriptorHandle* dh);
|
DescriptorHandle* dh);
|
||||||
|
|
|
@ -573,6 +573,7 @@ bool D3D12GPUDevice::UpdateImGuiFontTexture()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
bool D3D12GPUDevice::Render(bool skip_present)
|
bool D3D12GPUDevice::Render(bool skip_present)
|
||||||
{
|
{
|
||||||
if (skip_present || !m_swap_chain)
|
if (skip_present || !m_swap_chain)
|
||||||
|
@ -604,6 +605,7 @@ bool D3D12GPUDevice::Render(bool skip_present)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool D3D12GPUDevice::SetGPUTimingEnabled(bool enabled)
|
bool D3D12GPUDevice::SetGPUTimingEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,7 +59,7 @@ public:
|
||||||
|
|
||||||
void SetVSync(bool enabled) override;
|
void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
bool Render(bool skip_present) override;
|
//bool Render(bool skip_present) override;
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
|
#include "../opengl_loader.h"
|
||||||
|
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "loader.h"
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -11,7 +13,6 @@
|
||||||
#else
|
#else
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
Log_SetChannel(GL::Context);
|
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(_M_ARM64)
|
#if defined(_WIN32) && !defined(_M_ARM64)
|
||||||
#include "context_wgl.h"
|
#include "context_wgl.h"
|
||||||
|
@ -32,6 +33,8 @@ Log_SetChannel(GL::Context);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Log_SetChannel(GL::Context);
|
||||||
|
|
||||||
namespace GL {
|
namespace GL {
|
||||||
|
|
||||||
static bool ShouldPreferESContext()
|
static bool ShouldPreferESContext()
|
||||||
|
@ -84,7 +87,9 @@ static void DisableBrokenExtensions(const char* gl_vendor, const char* gl_render
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::Context(const WindowInfo& wi) : m_wi(wi) {}
|
Context::Context(const WindowInfo& wi) : m_wi(wi)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Context::~Context() = default;
|
Context::~Context() = default;
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "context_wgl.h"
|
#include "context_wgl.h"
|
||||||
|
#include "../opengl_loader.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/scoped_guard.h"
|
#include "common/scoped_guard.h"
|
||||||
#include "loader.h"
|
|
||||||
Log_SetChannel(GL::ContextWGL);
|
Log_SetChannel(GL::ContextWGL);
|
||||||
|
|
||||||
// TODO: get rid of this
|
// TODO: get rid of this
|
||||||
|
@ -33,7 +35,9 @@ static bool ReloadWGL(HDC dc)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace GL {
|
namespace GL {
|
||||||
ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi) {}
|
ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ContextWGL::~ContextWGL()
|
ContextWGL::~ContextWGL()
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "common/windows_headers.h"
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
|
|
||||||
|
#include "../opengl_loader.h"
|
||||||
|
|
||||||
|
#include "common/windows_headers.h"
|
||||||
|
|
||||||
#include "glad_wgl.h"
|
#include "glad_wgl.h"
|
||||||
#include "loader.h"
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace GL {
|
namespace GL {
|
||||||
|
|
|
@ -1,622 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#include "program.h"
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/log.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
#include <array>
|
|
||||||
#include <fstream>
|
|
||||||
Log_SetChannel(GL);
|
|
||||||
|
|
||||||
namespace GL {
|
|
||||||
|
|
||||||
GLuint Program::s_last_program_id = 0;
|
|
||||||
static GLuint s_next_bad_shader_id = 1;
|
|
||||||
|
|
||||||
Program::Program() = default;
|
|
||||||
|
|
||||||
Program::Program(Program&& prog)
|
|
||||||
{
|
|
||||||
m_program_id = prog.m_program_id;
|
|
||||||
prog.m_program_id = 0;
|
|
||||||
m_vertex_shader_id = prog.m_vertex_shader_id;
|
|
||||||
prog.m_vertex_shader_id = 0;
|
|
||||||
m_fragment_shader_id = prog.m_fragment_shader_id;
|
|
||||||
prog.m_fragment_shader_id = 0;
|
|
||||||
m_uniform_locations = std::move(prog.m_uniform_locations);
|
|
||||||
}
|
|
||||||
|
|
||||||
Program::~Program()
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint Program::CompileShader(GLenum type, const std::string_view source)
|
|
||||||
{
|
|
||||||
GLuint id = glCreateShader(type);
|
|
||||||
|
|
||||||
std::array<const GLchar*, 1> sources = {{source.data()}};
|
|
||||||
std::array<GLint, 1> source_lengths = {{static_cast<GLint>(source.size())}};
|
|
||||||
glShaderSource(id, static_cast<GLsizei>(sources.size()), sources.data(), source_lengths.data());
|
|
||||||
glCompileShader(id);
|
|
||||||
|
|
||||||
GLint status = GL_FALSE;
|
|
||||||
glGetShaderiv(id, GL_COMPILE_STATUS, &status);
|
|
||||||
|
|
||||||
GLint info_log_length = 0;
|
|
||||||
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length);
|
|
||||||
|
|
||||||
if (status == GL_FALSE || info_log_length > 0)
|
|
||||||
{
|
|
||||||
std::string info_log;
|
|
||||||
info_log.resize(info_log_length + 1);
|
|
||||||
glGetShaderInfoLog(id, info_log_length, &info_log_length, &info_log[0]);
|
|
||||||
|
|
||||||
if (status == GL_TRUE)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Shader compiled with warnings:\n%s", info_log.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Shader failed to compile:\n%s", info_log.c_str());
|
|
||||||
|
|
||||||
std::ofstream ofs(StringUtil::StdStringFromFormat("bad_shader_%u.txt", s_next_bad_shader_id++).c_str(),
|
|
||||||
std::ofstream::out | std::ofstream::binary);
|
|
||||||
if (ofs.is_open())
|
|
||||||
{
|
|
||||||
ofs.write(sources[0], source_lengths[0]);
|
|
||||||
ofs << "\n\nCompile failed, info log:\n";
|
|
||||||
ofs << info_log;
|
|
||||||
ofs.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
glDeleteShader(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::ResetLastProgram()
|
|
||||||
{
|
|
||||||
s_last_program_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Program::Compile(const std::string_view vertex_shader,const std::string_view fragment_shader)
|
|
||||||
{
|
|
||||||
if (m_vertex_shader_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteShader(m_vertex_shader_id);
|
|
||||||
m_vertex_shader_id = 0;
|
|
||||||
}
|
|
||||||
if (m_fragment_shader_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteShader(m_fragment_shader_id);
|
|
||||||
m_fragment_shader_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!vertex_shader.empty())
|
|
||||||
{
|
|
||||||
m_vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader);
|
|
||||||
if (m_vertex_shader_id == 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fragment_shader.empty())
|
|
||||||
{
|
|
||||||
m_fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader);
|
|
||||||
if (m_fragment_shader_id == 0)
|
|
||||||
{
|
|
||||||
glDeleteShader(m_fragment_shader_id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_program_id = glCreateProgram();
|
|
||||||
if (m_vertex_shader_id != 0)
|
|
||||||
glAttachShader(m_program_id, m_vertex_shader_id);
|
|
||||||
if (m_fragment_shader_id != 0)
|
|
||||||
glAttachShader(m_program_id, m_fragment_shader_id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Program::CreateFromBinary(const void* data, u32 data_length, u32 data_format)
|
|
||||||
{
|
|
||||||
GLuint prog = glCreateProgram();
|
|
||||||
glProgramBinary(prog, static_cast<GLenum>(data_format), data, data_length);
|
|
||||||
|
|
||||||
GLint link_status;
|
|
||||||
glGetProgramiv(prog, GL_LINK_STATUS, &link_status);
|
|
||||||
if (link_status != GL_TRUE)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to create GL program from binary: status %d", link_status);
|
|
||||||
glDeleteProgram(prog);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_program_id = prog;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Program::GetBinary(std::vector<u8>* out_data, u32* out_data_format)
|
|
||||||
{
|
|
||||||
GLint binary_size = 0;
|
|
||||||
glGetProgramiv(m_program_id, GL_PROGRAM_BINARY_LENGTH, &binary_size);
|
|
||||||
if (binary_size == 0)
|
|
||||||
{
|
|
||||||
Log_WarningPrint("glGetProgramiv(GL_PROGRAM_BINARY_LENGTH) returned 0");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLenum format = 0;
|
|
||||||
out_data->resize(static_cast<size_t>(binary_size));
|
|
||||||
glGetProgramBinary(m_program_id, binary_size, &binary_size, &format, out_data->data());
|
|
||||||
if (binary_size == 0)
|
|
||||||
{
|
|
||||||
Log_WarningPrint("glGetProgramBinary() failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (static_cast<size_t>(binary_size) != out_data->size())
|
|
||||||
{
|
|
||||||
Log_WarningPrintf("Size changed from %zu to %d after glGetProgramBinary()", out_data->size(), binary_size);
|
|
||||||
out_data->resize(static_cast<size_t>(binary_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_data_format = static_cast<u32>(format);
|
|
||||||
Log_DevPrintf("Program binary retrieved, %zu bytes, format %u", out_data->size(), *out_data_format);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::SetBinaryRetrievableHint()
|
|
||||||
{
|
|
||||||
glProgramParameteri(m_program_id, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::BindAttribute(GLuint index, const char* name)
|
|
||||||
{
|
|
||||||
glBindAttribLocation(m_program_id, index, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::BindDefaultAttributes()
|
|
||||||
{
|
|
||||||
BindAttribute(0, "a_position");
|
|
||||||
BindAttribute(1, "a_texcoord");
|
|
||||||
BindAttribute(2, "a_color");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::BindFragData(GLuint index /*= 0*/, const char* name /*= "o_col0"*/)
|
|
||||||
{
|
|
||||||
glBindFragDataLocation(m_program_id, index, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::BindFragDataIndexed(GLuint color_number /*= 0*/, const char* name /*= "o_col0"*/)
|
|
||||||
{
|
|
||||||
if (GLAD_GL_VERSION_3_3 || GLAD_GL_ARB_blend_func_extended)
|
|
||||||
{
|
|
||||||
glBindFragDataLocationIndexed(m_program_id, color_number, 0, name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (GLAD_GL_EXT_blend_func_extended)
|
|
||||||
{
|
|
||||||
glBindFragDataLocationIndexedEXT(m_program_id, color_number, 0, name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log_ErrorPrintf("BindFragDataIndexed() called without ARB or EXT extension, we'll probably crash.");
|
|
||||||
glBindFragDataLocationIndexed(m_program_id, color_number, 0, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Program::Link()
|
|
||||||
{
|
|
||||||
glLinkProgram(m_program_id);
|
|
||||||
|
|
||||||
if (m_vertex_shader_id != 0)
|
|
||||||
glDeleteShader(m_vertex_shader_id);
|
|
||||||
m_vertex_shader_id = 0;
|
|
||||||
if (m_fragment_shader_id != 0)
|
|
||||||
glDeleteShader(m_fragment_shader_id);
|
|
||||||
m_fragment_shader_id = 0;
|
|
||||||
|
|
||||||
GLint status = GL_FALSE;
|
|
||||||
glGetProgramiv(m_program_id, GL_LINK_STATUS, &status);
|
|
||||||
|
|
||||||
GLint info_log_length = 0;
|
|
||||||
glGetProgramiv(m_program_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
|
||||||
|
|
||||||
if (status == GL_FALSE || info_log_length > 0)
|
|
||||||
{
|
|
||||||
std::string info_log;
|
|
||||||
info_log.resize(info_log_length + 1);
|
|
||||||
glGetProgramInfoLog(m_program_id, info_log_length, &info_log_length, &info_log[0]);
|
|
||||||
|
|
||||||
if (status == GL_TRUE)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Program linked with warnings:\n%s", info_log.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Program failed to link:\n%s", info_log.c_str());
|
|
||||||
glDeleteProgram(m_program_id);
|
|
||||||
m_program_id = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Bind() const
|
|
||||||
{
|
|
||||||
if (s_last_program_id == m_program_id)
|
|
||||||
return;
|
|
||||||
|
|
||||||
glUseProgram(m_program_id);
|
|
||||||
s_last_program_id = m_program_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Destroy()
|
|
||||||
{
|
|
||||||
if (m_vertex_shader_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteShader(m_vertex_shader_id);
|
|
||||||
m_vertex_shader_id = 0;
|
|
||||||
}
|
|
||||||
if (m_fragment_shader_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteShader(m_fragment_shader_id);
|
|
||||||
m_fragment_shader_id = 0;
|
|
||||||
}
|
|
||||||
if (m_program_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteProgram(m_program_id);
|
|
||||||
m_program_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_uniform_locations.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Program::RegisterUniform(const char* name)
|
|
||||||
{
|
|
||||||
int id = static_cast<int>(m_uniform_locations.size());
|
|
||||||
m_uniform_locations.push_back(glGetUniformLocation(m_program_id, name));
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform1ui(int index, u32 x) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform1ui(location, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2ui(int index, u32 x, u32 y) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2ui(location, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3ui(int index, u32 x, u32 y, u32 z) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3ui(location, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4ui(int index, u32 x, u32 y, u32 z, u32 w) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4ui(location, x, y, z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform1i(int index, s32 x) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform1i(location, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2i(int index, s32 x, s32 y) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2i(location, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3i(int index, s32 x, s32 y, s32 z) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3i(location, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4i(int index, s32 x, s32 y, s32 z, s32 w) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4i(location, x, y, z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform1f(int index, float x) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform1f(location, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2f(int index, float x, float y) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2f(location, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3f(int index, float x, float y, float z) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3f(location, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4f(int index, float x, float y, float z, float w) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4f(location, x, y, z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2uiv(int index, const u32* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2uiv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3uiv(int index, const u32* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3uiv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4uiv(int index, const u32* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4uiv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2iv(int index, const s32* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2iv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3iv(int index, const s32* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3iv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4iv(int index, const s32* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4iv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2fv(int index, const float* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2fv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3fv(int index, const float* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3fv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4fv(int index, const float* v) const
|
|
||||||
{
|
|
||||||
Assert(static_cast<size_t>(index) < m_uniform_locations.size());
|
|
||||||
const GLint location = m_uniform_locations[index];
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4fv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform1ui(const char* name, u32 x) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform1ui(location, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2ui(const char* name, u32 x, u32 y) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2ui(location, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3ui(const char* name, u32 x, u32 y, u32 z) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3ui(location, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4ui(location, x, y, z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform1i(const char* name, s32 x) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform1i(location, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2i(const char* name, s32 x, s32 y) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2i(location, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3i(const char* name, s32 x, s32 y, s32 z) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3i(location, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4i(location, x, y, z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform1f(const char* name, float x) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform1f(location, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2f(const char* name, float x, float y) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2f(location, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3f(const char* name, float x, float y, float z) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3f(location, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4f(const char* name, float x, float y, float z, float w) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4f(location, x, y, z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2uiv(const char* name, const u32* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2uiv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3uiv(const char* name, const u32* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3uiv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4uiv(const char* name, const u32* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4uiv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2iv(const char* name, const s32* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2iv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3iv(const char* name, const s32* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3iv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4iv(const char* name, const s32* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4iv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform2fv(const char* name, const float* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform2fv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform3fv(const char* name, const float* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform3fv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::Uniform4fv(const char* name, const float* v) const
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniform4fv(location, 1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::BindUniformBlock(const char* name, u32 index)
|
|
||||||
{
|
|
||||||
const GLint location = glGetUniformBlockIndex(m_program_id, name);
|
|
||||||
if (location >= 0)
|
|
||||||
glUniformBlockBinding(m_program_id, location, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Program& Program::operator=(Program&& prog)
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
m_program_id = prog.m_program_id;
|
|
||||||
prog.m_program_id = 0;
|
|
||||||
m_vertex_shader_id = prog.m_vertex_shader_id;
|
|
||||||
prog.m_vertex_shader_id = 0;
|
|
||||||
m_fragment_shader_id = prog.m_fragment_shader_id;
|
|
||||||
prog.m_fragment_shader_id = 0;
|
|
||||||
m_uniform_locations = std::move(prog.m_uniform_locations);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace GL
|
|
|
@ -1,104 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "common/types.h"
|
|
||||||
#include "loader.h"
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace GL {
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Program();
|
|
||||||
Program(const Program&) = delete;
|
|
||||||
Program(Program&& prog);
|
|
||||||
~Program();
|
|
||||||
|
|
||||||
static GLuint CompileShader(GLenum type, const std::string_view source);
|
|
||||||
static void ResetLastProgram();
|
|
||||||
|
|
||||||
bool IsVaild() const { return m_program_id != 0; }
|
|
||||||
bool IsBound() const { return s_last_program_id == m_program_id; }
|
|
||||||
|
|
||||||
bool Compile(const std::string_view vertex_shader, const std::string_view fragment_shader);
|
|
||||||
|
|
||||||
bool CreateFromBinary(const void* data, u32 data_length, u32 data_format);
|
|
||||||
|
|
||||||
bool GetBinary(std::vector<u8>* out_data, u32* out_data_format);
|
|
||||||
void SetBinaryRetrievableHint();
|
|
||||||
|
|
||||||
void BindAttribute(GLuint index, const char* name);
|
|
||||||
void BindDefaultAttributes();
|
|
||||||
|
|
||||||
void BindFragData(GLuint index = 0, const char* name = "o_col0");
|
|
||||||
void BindFragDataIndexed(GLuint color_number = 0, const char* name = "o_col0");
|
|
||||||
|
|
||||||
bool Link();
|
|
||||||
|
|
||||||
void Bind() const;
|
|
||||||
|
|
||||||
void Destroy();
|
|
||||||
|
|
||||||
int RegisterUniform(const char* name);
|
|
||||||
void Uniform1ui(int index, u32 x) const;
|
|
||||||
void Uniform2ui(int index, u32 x, u32 y) const;
|
|
||||||
void Uniform3ui(int index, u32 x, u32 y, u32 z) const;
|
|
||||||
void Uniform4ui(int index, u32 x, u32 y, u32 z, u32 w) const;
|
|
||||||
void Uniform1i(int index, s32 x) const;
|
|
||||||
void Uniform2i(int index, s32 x, s32 y) const;
|
|
||||||
void Uniform3i(int index, s32 x, s32 y, s32 z) const;
|
|
||||||
void Uniform4i(int index, s32 x, s32 y, s32 z, s32 w) const;
|
|
||||||
void Uniform1f(int index, float x) const;
|
|
||||||
void Uniform2f(int index, float x, float y) const;
|
|
||||||
void Uniform3f(int index, float x, float y, float z) const;
|
|
||||||
void Uniform4f(int index, float x, float y, float z, float w) const;
|
|
||||||
void Uniform2uiv(int index, const u32* v) const;
|
|
||||||
void Uniform3uiv(int index, const u32* v) const;
|
|
||||||
void Uniform4uiv(int index, const u32* v) const;
|
|
||||||
void Uniform2iv(int index, const s32* v) const;
|
|
||||||
void Uniform3iv(int index, const s32* v) const;
|
|
||||||
void Uniform4iv(int index, const s32* v) const;
|
|
||||||
void Uniform2fv(int index, const float* v) const;
|
|
||||||
void Uniform3fv(int index, const float* v) const;
|
|
||||||
void Uniform4fv(int index, const float* v) const;
|
|
||||||
|
|
||||||
void Uniform1ui(const char* name, u32 x) const;
|
|
||||||
void Uniform2ui(const char* name, u32 x, u32 y) const;
|
|
||||||
void Uniform3ui(const char* name, u32 x, u32 y, u32 z) const;
|
|
||||||
void Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const;
|
|
||||||
void Uniform1i(const char* name, s32 x) const;
|
|
||||||
void Uniform2i(const char* name, s32 x, s32 y) const;
|
|
||||||
void Uniform3i(const char* name, s32 x, s32 y, s32 z) const;
|
|
||||||
void Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const;
|
|
||||||
void Uniform1f(const char* name, float x) const;
|
|
||||||
void Uniform2f(const char* name, float x, float y) const;
|
|
||||||
void Uniform3f(const char* name, float x, float y, float z) const;
|
|
||||||
void Uniform4f(const char* name, float x, float y, float z, float w) const;
|
|
||||||
void Uniform2uiv(const char* name, const u32* v) const;
|
|
||||||
void Uniform3uiv(const char* name, const u32* v) const;
|
|
||||||
void Uniform4uiv(const char* name, const u32* v) const;
|
|
||||||
void Uniform2iv(const char* name, const s32* v) const;
|
|
||||||
void Uniform3iv(const char* name, const s32* v) const;
|
|
||||||
void Uniform4iv(const char* name, const s32* v) const;
|
|
||||||
void Uniform2fv(const char* name, const float* v) const;
|
|
||||||
void Uniform3fv(const char* name, const float* v) const;
|
|
||||||
void Uniform4fv(const char* name, const float* v) const;
|
|
||||||
|
|
||||||
void BindUniformBlock(const char* name, u32 index);
|
|
||||||
|
|
||||||
Program& operator=(const Program&) = delete;
|
|
||||||
Program& operator=(Program&& prog);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static u32 s_last_program_id;
|
|
||||||
|
|
||||||
GLuint m_program_id = 0;
|
|
||||||
GLuint m_vertex_shader_id = 0;
|
|
||||||
GLuint m_fragment_shader_id = 0;
|
|
||||||
|
|
||||||
std::vector<GLint> m_uniform_locations;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace GL
|
|
|
@ -1,344 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#include "shader_cache.h"
|
|
||||||
#include "common/file_system.h"
|
|
||||||
#include "common/log.h"
|
|
||||||
#include "common/md5_digest.h"
|
|
||||||
#include "common/path.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
Log_SetChannel(GL::ShaderCache);
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct CacheIndexEntry
|
|
||||||
{
|
|
||||||
u64 vertex_source_hash_low;
|
|
||||||
u64 vertex_source_hash_high;
|
|
||||||
u32 vertex_source_length;
|
|
||||||
u64 geometry_source_hash_low;
|
|
||||||
u64 geometry_source_hash_high;
|
|
||||||
u32 geometry_source_length;
|
|
||||||
u64 fragment_source_hash_low;
|
|
||||||
u64 fragment_source_hash_high;
|
|
||||||
u32 fragment_source_length;
|
|
||||||
u32 file_offset;
|
|
||||||
u32 blob_size;
|
|
||||||
u32 blob_format;
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
GL::ShaderCache::ShaderCache() = default;
|
|
||||||
|
|
||||||
GL::ShaderCache::~ShaderCache()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
vertex_source_hash_low == key.vertex_source_hash_low && vertex_source_hash_high == key.vertex_source_hash_high &&
|
|
||||||
vertex_source_length == key.vertex_source_length && fragment_source_hash_low == key.fragment_source_hash_low &&
|
|
||||||
fragment_source_hash_high == key.fragment_source_hash_high && fragment_source_length == key.fragment_source_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
vertex_source_hash_low != key.vertex_source_hash_low || vertex_source_hash_high != key.vertex_source_hash_high ||
|
|
||||||
vertex_source_length != key.vertex_source_length || fragment_source_hash_low != key.fragment_source_hash_low ||
|
|
||||||
fragment_source_hash_high != key.fragment_source_hash_high || fragment_source_length != key.fragment_source_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::ShaderCache::Open(bool is_gles, std::string_view base_path, u32 version)
|
|
||||||
{
|
|
||||||
m_base_path = base_path;
|
|
||||||
m_version = version;
|
|
||||||
m_program_binary_supported = is_gles || GLAD_GL_ARB_get_program_binary;
|
|
||||||
if (m_program_binary_supported)
|
|
||||||
{
|
|
||||||
// check that there's at least one format and the extension isn't being "faked"
|
|
||||||
GLint num_formats = 0;
|
|
||||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
|
|
||||||
Log_InfoPrintf("%u program binary formats supported by driver", num_formats);
|
|
||||||
m_program_binary_supported = (num_formats > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_program_binary_supported)
|
|
||||||
{
|
|
||||||
Log_WarningPrintf("Your GL driver does not support program binaries. Hopefully it has a built-in cache, otherwise "
|
|
||||||
"startup will be slow due to compiling shaders.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!base_path.empty())
|
|
||||||
{
|
|
||||||
const std::string index_filename = GetIndexFileName();
|
|
||||||
const std::string blob_filename = GetBlobFileName();
|
|
||||||
|
|
||||||
if (!ReadExisting(index_filename, blob_filename))
|
|
||||||
CreateNew(index_filename, blob_filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename)
|
|
||||||
{
|
|
||||||
if (FileSystem::FileExists(index_filename.c_str()))
|
|
||||||
{
|
|
||||||
Log_WarningPrintf("Removing existing index file '%s'", index_filename.c_str());
|
|
||||||
FileSystem::DeleteFile(index_filename.c_str());
|
|
||||||
}
|
|
||||||
if (FileSystem::FileExists(blob_filename.c_str()))
|
|
||||||
{
|
|
||||||
Log_WarningPrintf("Removing existing blob file '%s'", blob_filename.c_str());
|
|
||||||
FileSystem::DeleteFile(blob_filename.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "wb");
|
|
||||||
if (!m_index_file)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to open index file '%s' for writing", index_filename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 index_version = FILE_VERSION;
|
|
||||||
if (std::fwrite(&index_version, sizeof(index_version), 1, m_index_file) != 1 ||
|
|
||||||
std::fwrite(&m_version, sizeof(m_version), 1, m_index_file) != 1)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to write version to index file '%s'", index_filename.c_str());
|
|
||||||
std::fclose(m_index_file);
|
|
||||||
m_index_file = nullptr;
|
|
||||||
FileSystem::DeleteFile(index_filename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "w+b");
|
|
||||||
if (!m_blob_file)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to open blob file '%s' for writing", blob_filename.c_str());
|
|
||||||
std::fclose(m_index_file);
|
|
||||||
m_index_file = nullptr;
|
|
||||||
FileSystem::DeleteFile(index_filename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::ShaderCache::ReadExisting(const std::string& index_filename, const std::string& blob_filename)
|
|
||||||
{
|
|
||||||
m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "r+b");
|
|
||||||
if (!m_index_file)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
u32 file_version = 0;
|
|
||||||
u32 data_version = 0;
|
|
||||||
if (std::fread(&file_version, sizeof(file_version), 1, m_index_file) != 1 || file_version != FILE_VERSION ||
|
|
||||||
std::fread(&data_version, sizeof(data_version), 1, m_index_file) != 1 || data_version != m_version)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Bad file/data version in '%s'", index_filename.c_str());
|
|
||||||
std::fclose(m_index_file);
|
|
||||||
m_index_file = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "a+b");
|
|
||||||
if (!m_blob_file)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Blob file '%s' is missing", blob_filename.c_str());
|
|
||||||
std::fclose(m_index_file);
|
|
||||||
m_index_file = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::fseek(m_blob_file, 0, SEEK_END);
|
|
||||||
const u32 blob_file_size = static_cast<u32>(std::ftell(m_blob_file));
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
CacheIndexEntry entry;
|
|
||||||
if (std::fread(&entry, sizeof(entry), 1, m_index_file) != 1 ||
|
|
||||||
(entry.file_offset + entry.blob_size) > blob_file_size)
|
|
||||||
{
|
|
||||||
if (std::feof(m_index_file))
|
|
||||||
break;
|
|
||||||
|
|
||||||
Log_ErrorPrintf("Failed to read entry from '%s', corrupt file?", index_filename.c_str());
|
|
||||||
m_index.clear();
|
|
||||||
std::fclose(m_blob_file);
|
|
||||||
m_blob_file = nullptr;
|
|
||||||
std::fclose(m_index_file);
|
|
||||||
m_index_file = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CacheIndexKey key{entry.vertex_source_hash_low, entry.vertex_source_hash_high,
|
|
||||||
entry.vertex_source_length, entry.fragment_source_hash_low,
|
|
||||||
entry.fragment_source_hash_high, entry.fragment_source_length};
|
|
||||||
const CacheIndexData data{entry.file_offset, entry.blob_size, entry.blob_format};
|
|
||||||
m_index.emplace(key, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log_InfoPrintf("Read %zu entries from '%s'", m_index.size(), index_filename.c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::ShaderCache::Close()
|
|
||||||
{
|
|
||||||
m_index.clear();
|
|
||||||
if (m_index_file)
|
|
||||||
std::fclose(m_index_file);
|
|
||||||
if (m_blob_file)
|
|
||||||
std::fclose(m_blob_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::ShaderCache::Recreate()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
|
|
||||||
const std::string index_filename = GetIndexFileName();
|
|
||||||
const std::string blob_filename = GetBlobFileName();
|
|
||||||
|
|
||||||
return CreateNew(index_filename, blob_filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
GL::ShaderCache::CacheIndexKey GL::ShaderCache::GetCacheKey(const std::string_view& vertex_shader,
|
|
||||||
const std::string_view& fragment_shader)
|
|
||||||
{
|
|
||||||
union ShaderHash
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 low;
|
|
||||||
u64 high;
|
|
||||||
};
|
|
||||||
u8 bytes[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
ShaderHash vertex_hash = {};
|
|
||||||
ShaderHash fragment_hash = {};
|
|
||||||
|
|
||||||
MD5Digest digest;
|
|
||||||
if (!vertex_shader.empty())
|
|
||||||
{
|
|
||||||
digest.Update(vertex_shader.data(), static_cast<u32>(vertex_shader.length()));
|
|
||||||
digest.Final(vertex_hash.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fragment_shader.empty())
|
|
||||||
{
|
|
||||||
digest.Reset();
|
|
||||||
digest.Update(fragment_shader.data(), static_cast<u32>(fragment_shader.length()));
|
|
||||||
digest.Final(fragment_hash.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CacheIndexKey{vertex_hash.low, vertex_hash.high, static_cast<u32>(vertex_shader.length()),
|
|
||||||
fragment_hash.low, fragment_hash.high, static_cast<u32>(fragment_shader.length())};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GL::ShaderCache::GetIndexFileName() const
|
|
||||||
{
|
|
||||||
return Path::Combine(m_base_path, "gl_programs.idx");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GL::ShaderCache::GetBlobFileName() const
|
|
||||||
{
|
|
||||||
return Path::Combine(m_base_path, "gl_programs.bin");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<GL::Program> GL::ShaderCache::GetProgram(const std::string_view vertex_shader,
|
|
||||||
const std::string_view fragment_shader,
|
|
||||||
const PreLinkCallback& callback)
|
|
||||||
{
|
|
||||||
if (!m_program_binary_supported || !m_blob_file)
|
|
||||||
return CompileProgram(vertex_shader, fragment_shader, callback, false);
|
|
||||||
|
|
||||||
const auto key = GetCacheKey(vertex_shader, fragment_shader);
|
|
||||||
auto iter = m_index.find(key);
|
|
||||||
if (iter == m_index.end())
|
|
||||||
return CompileAndAddProgram(key, vertex_shader, fragment_shader, callback);
|
|
||||||
|
|
||||||
std::vector<u8> data(iter->second.blob_size);
|
|
||||||
if (std::fseek(m_blob_file, iter->second.file_offset, SEEK_SET) != 0 ||
|
|
||||||
std::fread(data.data(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Read blob from file failed");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Program prog;
|
|
||||||
if (prog.CreateFromBinary(data.data(), static_cast<u32>(data.size()), iter->second.blob_format))
|
|
||||||
return std::optional<Program>(std::move(prog));
|
|
||||||
|
|
||||||
Log_WarningPrintf(
|
|
||||||
"Failed to create program from binary, this may be due to a driver or GPU Change. Recreating cache.");
|
|
||||||
if (!Recreate())
|
|
||||||
return CompileProgram(vertex_shader, fragment_shader, callback, false);
|
|
||||||
else
|
|
||||||
return CompileAndAddProgram(key, vertex_shader, fragment_shader, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<GL::Program> GL::ShaderCache::CompileProgram(const std::string_view& vertex_shader,
|
|
||||||
const std::string_view& fragment_shader,
|
|
||||||
const PreLinkCallback& callback, bool set_retrievable)
|
|
||||||
{
|
|
||||||
Program prog;
|
|
||||||
if (!prog.Compile(vertex_shader, fragment_shader))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
if (callback)
|
|
||||||
callback(prog);
|
|
||||||
|
|
||||||
if (set_retrievable)
|
|
||||||
prog.SetBinaryRetrievableHint();
|
|
||||||
|
|
||||||
if (!prog.Link())
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
return std::optional<Program>(std::move(prog));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<GL::Program> GL::ShaderCache::CompileAndAddProgram(const CacheIndexKey& key,
|
|
||||||
const std::string_view& vertex_shader,
|
|
||||||
const std::string_view& fragment_shader,
|
|
||||||
const PreLinkCallback& callback)
|
|
||||||
{
|
|
||||||
std::optional<Program> prog = CompileProgram(vertex_shader, fragment_shader, callback, true);
|
|
||||||
if (!prog)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
std::vector<u8> prog_data;
|
|
||||||
u32 prog_format = 0;
|
|
||||||
if (!prog->GetBinary(&prog_data, &prog_format))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
if (!m_blob_file || std::fseek(m_blob_file, 0, SEEK_END) != 0)
|
|
||||||
return prog;
|
|
||||||
|
|
||||||
CacheIndexData data;
|
|
||||||
data.file_offset = static_cast<u32>(std::ftell(m_blob_file));
|
|
||||||
data.blob_size = static_cast<u32>(prog_data.size());
|
|
||||||
data.blob_format = prog_format;
|
|
||||||
|
|
||||||
CacheIndexEntry entry = {};
|
|
||||||
entry.vertex_source_hash_low = key.vertex_source_hash_low;
|
|
||||||
entry.vertex_source_hash_high = key.vertex_source_hash_high;
|
|
||||||
entry.vertex_source_length = key.vertex_source_length;
|
|
||||||
entry.fragment_source_hash_low = key.fragment_source_hash_low;
|
|
||||||
entry.fragment_source_hash_high = key.fragment_source_hash_high;
|
|
||||||
entry.fragment_source_length = key.fragment_source_length;
|
|
||||||
entry.file_offset = data.file_offset;
|
|
||||||
entry.blob_size = data.blob_size;
|
|
||||||
entry.blob_format = data.blob_format;
|
|
||||||
|
|
||||||
if (std::fwrite(prog_data.data(), 1, entry.blob_size, m_blob_file) != entry.blob_size ||
|
|
||||||
std::fflush(m_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 ||
|
|
||||||
std::fflush(m_index_file) != 0)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to write shader blob to file");
|
|
||||||
return prog;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_index.emplace(key, data);
|
|
||||||
return prog;
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "common/hash_combine.h"
|
|
||||||
#include "common/types.h"
|
|
||||||
#include "program.h"
|
|
||||||
#include <cstdio>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace GL {
|
|
||||||
|
|
||||||
class ShaderCache
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using PreLinkCallback = std::function<void(Program&)>;
|
|
||||||
|
|
||||||
ShaderCache();
|
|
||||||
~ShaderCache();
|
|
||||||
|
|
||||||
void Open(bool is_gles, std::string_view base_path, u32 version);
|
|
||||||
|
|
||||||
std::optional<Program> GetProgram(const std::string_view vertex_shader, const std::string_view fragment_shader,
|
|
||||||
const PreLinkCallback& callback = {});
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr u32 FILE_VERSION = 4;
|
|
||||||
|
|
||||||
struct CacheIndexKey
|
|
||||||
{
|
|
||||||
u64 vertex_source_hash_low;
|
|
||||||
u64 vertex_source_hash_high;
|
|
||||||
u32 vertex_source_length;
|
|
||||||
u64 fragment_source_hash_low;
|
|
||||||
u64 fragment_source_hash_high;
|
|
||||||
u32 fragment_source_length;
|
|
||||||
|
|
||||||
bool operator==(const CacheIndexKey& key) const;
|
|
||||||
bool operator!=(const CacheIndexKey& key) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CacheIndexEntryHasher
|
|
||||||
{
|
|
||||||
std::size_t operator()(const CacheIndexKey& e) const noexcept
|
|
||||||
{
|
|
||||||
std::size_t h = 0;
|
|
||||||
hash_combine(h, e.vertex_source_hash_low, e.vertex_source_hash_high, e.vertex_source_length,
|
|
||||||
e.fragment_source_hash_low, e.fragment_source_hash_high, e.fragment_source_length);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CacheIndexData
|
|
||||||
{
|
|
||||||
u32 file_offset;
|
|
||||||
u32 blob_size;
|
|
||||||
u32 blob_format;
|
|
||||||
};
|
|
||||||
|
|
||||||
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
|
|
||||||
|
|
||||||
static CacheIndexKey GetCacheKey(const std::string_view& vertex_shader, const std::string_view& fragment_shader);
|
|
||||||
|
|
||||||
std::string GetIndexFileName() const;
|
|
||||||
std::string GetBlobFileName() const;
|
|
||||||
|
|
||||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
|
||||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
|
||||||
void Close();
|
|
||||||
bool Recreate();
|
|
||||||
|
|
||||||
std::optional<Program> CompileProgram(const std::string_view& vertex_shader, const std::string_view& fragment_shader,
|
|
||||||
const PreLinkCallback& callback, bool set_retrievable);
|
|
||||||
std::optional<Program> CompileAndAddProgram(const CacheIndexKey& key, const std::string_view& vertex_shader,
|
|
||||||
const std::string_view& fragment_shader, const PreLinkCallback& callback);
|
|
||||||
|
|
||||||
std::string m_base_path;
|
|
||||||
std::FILE* m_index_file = nullptr;
|
|
||||||
std::FILE* m_blob_file = nullptr;
|
|
||||||
|
|
||||||
CacheIndex m_index;
|
|
||||||
u32 m_version = 0;
|
|
||||||
bool m_program_binary_supported = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace GL
|
|
|
@ -1,398 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#include "texture.h"
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/log.h"
|
|
||||||
#include <array>
|
|
||||||
#include <limits>
|
|
||||||
#include <tuple>
|
|
||||||
Log_SetChannel(GL);
|
|
||||||
|
|
||||||
const std::tuple<GLenum, GLenum, GLenum>& GL::Texture::GetPixelFormatMapping(GPUTexture::Format format)
|
|
||||||
{
|
|
||||||
static constexpr std::array<std::tuple<GLenum, GLenum, GLenum>, static_cast<u32>(GPUTexture::Format::Count)> mapping =
|
|
||||||
{{
|
|
||||||
{}, // Unknown
|
|
||||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
|
|
||||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8
|
|
||||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
|
||||||
{GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGBA5551
|
|
||||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8
|
|
||||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16
|
|
||||||
}};
|
|
||||||
|
|
||||||
static constexpr std::array<std::tuple<GLenum, GLenum, GLenum>, static_cast<u32>(GPUTexture::Format::Count)>
|
|
||||||
mapping_gles2 = {{
|
|
||||||
{}, // Unknown
|
|
||||||
{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
|
|
||||||
{}, // BGRA8
|
|
||||||
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
|
||||||
{}, // RGBA5551
|
|
||||||
{}, // R8
|
|
||||||
{}, // D16
|
|
||||||
}};
|
|
||||||
|
|
||||||
if (!GLAD_GL_ES_VERSION_2_0 || GLAD_GL_ES_VERSION_3_0)
|
|
||||||
return mapping[static_cast<u32>(format)];
|
|
||||||
else
|
|
||||||
return mapping_gles2[static_cast<u32>(format)];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/,
|
|
||||||
u32 level /*= 0*/)
|
|
||||||
{
|
|
||||||
UnreachableCode();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
|
|
||||||
u32 level /*= 0*/)
|
|
||||||
{
|
|
||||||
UnreachableCode();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::Unmap()
|
|
||||||
{
|
|
||||||
UnreachableCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
GL::Texture::Texture() = default;
|
|
||||||
|
|
||||||
GL::Texture::Texture(Texture&& moved) : m_id(moved.m_id), m_fbo_id(moved.m_fbo_id)
|
|
||||||
{
|
|
||||||
m_width = moved.m_width;
|
|
||||||
m_height = moved.m_height;
|
|
||||||
m_levels = moved.m_levels;
|
|
||||||
m_layers = moved.m_layers;
|
|
||||||
m_samples = moved.m_samples;
|
|
||||||
m_format = moved.m_format;
|
|
||||||
moved.m_id = 0;
|
|
||||||
moved.m_fbo_id = 0;
|
|
||||||
moved.ClearBaseProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
GL::Texture::~Texture()
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::Texture::UseTextureStorage(bool multisampled)
|
|
||||||
{
|
|
||||||
return GLAD_GL_ARB_texture_storage || (multisampled ? GLAD_GL_ES_VERSION_3_1 : GLAD_GL_ES_VERSION_3_0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::Texture::UseTextureStorage() const
|
|
||||||
{
|
|
||||||
return UseTextureStorage(IsMultisampled());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::Texture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Format format,
|
|
||||||
const void* data /* = nullptr */, u32 data_pitch /* = 0 */, bool linear /* = true */,
|
|
||||||
bool wrap /* = true */)
|
|
||||||
{
|
|
||||||
glGetError();
|
|
||||||
|
|
||||||
if (width > MAX_WIDTH || height > MAX_HEIGHT || layers > MAX_LAYERS || levels > MAX_LEVELS || samples > MAX_SAMPLES)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Invalid dimensions: %ux%ux%u %u %u", width, height, layers, levels, samples);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (samples > 1 && levels > 1)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Multisampled textures can't have mip levels");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layers > 1 && data)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Loading texture array data not currently supported");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLenum target = ((samples > 1) ? ((layers > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D_MULTISAMPLE_ARRAY) :
|
|
||||||
((layers > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
|
|
||||||
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(format);
|
|
||||||
|
|
||||||
GLuint id;
|
|
||||||
glGenTextures(1, &id);
|
|
||||||
glBindTexture(target, id);
|
|
||||||
|
|
||||||
if (samples > 1)
|
|
||||||
{
|
|
||||||
Assert(!data);
|
|
||||||
if (UseTextureStorage(true))
|
|
||||||
{
|
|
||||||
if (layers > 1)
|
|
||||||
glTexStorage3DMultisample(target, samples, gl_internal_format, width, height, layers, GL_FALSE);
|
|
||||||
else
|
|
||||||
glTexStorage2DMultisample(target, samples, gl_internal_format, width, height, GL_FALSE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (layers > 1)
|
|
||||||
glTexImage3DMultisample(target, samples, gl_internal_format, width, height, layers, GL_FALSE);
|
|
||||||
else
|
|
||||||
glTexImage2DMultisample(target, samples, gl_internal_format, width, height, GL_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (UseTextureStorage(false))
|
|
||||||
{
|
|
||||||
if (layers > 1)
|
|
||||||
glTexStorage3D(target, levels, gl_internal_format, width, height, layers);
|
|
||||||
else
|
|
||||||
glTexStorage2D(target, levels, gl_internal_format, width, height);
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
// TODO: Fix data for mipmaps here.
|
|
||||||
if (layers > 1)
|
|
||||||
glTexSubImage3D(target, 0, 0, 0, 0, width, height, layers, gl_format, gl_type, data);
|
|
||||||
else
|
|
||||||
glTexSubImage2D(target, 0, 0, 0, width, height, gl_format, gl_type, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < levels; i++)
|
|
||||||
{
|
|
||||||
// TODO: Fix data pointer here.
|
|
||||||
if (layers > 1)
|
|
||||||
glTexImage3D(target, i, gl_internal_format, width, height, layers, 0, gl_format, gl_type, data);
|
|
||||||
else
|
|
||||||
glTexImage2D(target, i, gl_internal_format, width, height, 0, gl_format, gl_type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This doesn't exist on GLES2.
|
|
||||||
if (!GLAD_GL_ES_VERSION_2_0 || GLAD_GL_ES_VERSION_3_0)
|
|
||||||
{
|
|
||||||
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
if (layers > 1)
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLenum error = glGetError();
|
|
||||||
if (error != GL_NO_ERROR)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to create texture: 0x%X", error);
|
|
||||||
glDeleteTextures(1, &id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsValid())
|
|
||||||
Destroy();
|
|
||||||
|
|
||||||
m_id = id;
|
|
||||||
m_width = static_cast<u16>(width);
|
|
||||||
m_height = static_cast<u16>(height);
|
|
||||||
m_layers = static_cast<u8>(layers);
|
|
||||||
m_levels = static_cast<u8>(levels);
|
|
||||||
m_samples = static_cast<u8>(samples);
|
|
||||||
m_format = format;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::Replace(u32 width, u32 height, GLenum internal_format, GLenum format, GLenum type, const void* data)
|
|
||||||
{
|
|
||||||
Assert(IsValid() && width < MAX_WIDTH && height < MAX_HEIGHT && m_layers == 1 && m_samples == 1 && m_levels == 1);
|
|
||||||
|
|
||||||
const bool size_changed = (width != m_width || height != m_height);
|
|
||||||
|
|
||||||
m_width = static_cast<u16>(width);
|
|
||||||
m_height = static_cast<u16>(height);
|
|
||||||
m_levels = 1;
|
|
||||||
|
|
||||||
const GLenum target = GetGLTarget();
|
|
||||||
glBindTexture(target, m_id);
|
|
||||||
|
|
||||||
if (UseTextureStorage())
|
|
||||||
{
|
|
||||||
if (size_changed)
|
|
||||||
{
|
|
||||||
if (m_layers > 0)
|
|
||||||
glTexStorage3D(target, m_levels, internal_format, m_width, m_height, m_levels);
|
|
||||||
else
|
|
||||||
glTexStorage2D(target, m_levels, internal_format, m_width, m_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
glTexSubImage2D(target, 0, 0, 0, m_width, m_height, format, type, data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glTexImage2D(target, 0, internal_format, width, height, 0, format, type, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::ReplaceImage(u32 layer, u32 level, GLenum format, GLenum type, const void* data)
|
|
||||||
{
|
|
||||||
Assert(IsValid() && !IsMultisampled());
|
|
||||||
|
|
||||||
const GLenum target = GetGLTarget();
|
|
||||||
if (IsTextureArray())
|
|
||||||
glTexSubImage3D(target, level, 0, 0, layer, m_width, m_height, 1, format, type, data);
|
|
||||||
else
|
|
||||||
glTexSubImage2D(target, level, 0, 0, m_width, m_height, format, type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::ReplaceSubImage(u32 layer, u32 level, u32 x, u32 y, u32 width, u32 height, GLenum format, GLenum type,
|
|
||||||
const void* data)
|
|
||||||
{
|
|
||||||
Assert(IsValid() && !IsMultisampled());
|
|
||||||
|
|
||||||
const GLenum target = GetGLTarget();
|
|
||||||
if (IsTextureArray())
|
|
||||||
glTexSubImage3D(target, level, x, y, layer, width, height, 1, format, type, data);
|
|
||||||
else
|
|
||||||
glTexSubImage2D(target, level, x, y, width, height, format, type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::SetLinearFilter(bool enabled) const
|
|
||||||
{
|
|
||||||
Assert(!IsMultisampled());
|
|
||||||
|
|
||||||
Bind();
|
|
||||||
|
|
||||||
const GLenum target = GetGLTarget();
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, enabled ? GL_LINEAR : GL_NEAREST);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, enabled ? GL_LINEAR : GL_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::SetWrap(bool enabled) const
|
|
||||||
{
|
|
||||||
const GLenum target = GetGLTarget();
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, enabled ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, enabled ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
if (m_layers > 1)
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_R, enabled ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL::Texture::CreateFramebuffer()
|
|
||||||
{
|
|
||||||
if (!IsValid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
glGetError();
|
|
||||||
|
|
||||||
GLuint fbo_id;
|
|
||||||
glGenFramebuffers(1, &fbo_id);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id);
|
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_id, 0);
|
|
||||||
if (glGetError() != GL_NO_ERROR || glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
||||||
{
|
|
||||||
glDeleteFramebuffers(1, &fbo_id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_fbo_id != 0)
|
|
||||||
glDeleteFramebuffers(1, &m_fbo_id);
|
|
||||||
|
|
||||||
m_fbo_id = fbo_id;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::Destroy()
|
|
||||||
{
|
|
||||||
if (m_fbo_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteFramebuffers(1, &m_fbo_id);
|
|
||||||
m_fbo_id = 0;
|
|
||||||
}
|
|
||||||
if (m_id != 0)
|
|
||||||
{
|
|
||||||
glDeleteTextures(1, &m_id);
|
|
||||||
m_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearBaseProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::Bind() const
|
|
||||||
{
|
|
||||||
glBindTexture(GetGLTarget(), m_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::BindFramebuffer(GLenum target /*= GL_DRAW_FRAMEBUFFER*/) const
|
|
||||||
{
|
|
||||||
DebugAssert(m_fbo_id != 0);
|
|
||||||
glBindFramebuffer(target, m_fbo_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::Unbind() const
|
|
||||||
{
|
|
||||||
glBindTexture(GetGLTarget(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
GL::Texture& GL::Texture::operator=(Texture&& moved)
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
|
|
||||||
m_id = moved.m_id;
|
|
||||||
m_fbo_id = moved.m_fbo_id;
|
|
||||||
m_width = moved.m_width;
|
|
||||||
m_height = moved.m_height;
|
|
||||||
m_layers = moved.m_layers;
|
|
||||||
m_levels = moved.m_levels;
|
|
||||||
m_samples = moved.m_samples;
|
|
||||||
|
|
||||||
moved.m_id = 0;
|
|
||||||
moved.m_fbo_id = 0;
|
|
||||||
moved.ClearBaseProperties();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL::Texture::GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
|
||||||
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
|
|
||||||
GLsizei bufSize, void* pixels)
|
|
||||||
{
|
|
||||||
if (GLAD_GL_VERSION_4_5 || GLAD_GL_ARB_get_texture_sub_image)
|
|
||||||
{
|
|
||||||
glGetTextureSubImage(texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize,
|
|
||||||
pixels);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLenum target = GL_READ_FRAMEBUFFER;
|
|
||||||
GLenum target_binding = GL_READ_FRAMEBUFFER_BINDING;
|
|
||||||
if (GLAD_GL_ES_VERSION_2_0 && !GLAD_GL_ES_VERSION_3_0)
|
|
||||||
{
|
|
||||||
// GLES2 doesn't have GL_READ_FRAMEBUFFER.
|
|
||||||
target = GL_FRAMEBUFFER;
|
|
||||||
target_binding = GL_FRAMEBUFFER_BINDING;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert(depth == 1);
|
|
||||||
|
|
||||||
GLuint old_read_fbo;
|
|
||||||
glGetIntegerv(target_binding, reinterpret_cast<GLint*>(&old_read_fbo));
|
|
||||||
|
|
||||||
GLuint temp_fbo;
|
|
||||||
glGenFramebuffers(1, &temp_fbo);
|
|
||||||
glBindFramebuffer(target, temp_fbo);
|
|
||||||
if (zoffset > 0 && (GLAD_GL_VERSION_3_0 || GLAD_GL_ES_VERSION_3_0))
|
|
||||||
glFramebufferTextureLayer(target, GL_COLOR_ATTACHMENT0, texture, level, zoffset);
|
|
||||||
else
|
|
||||||
glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
|
|
||||||
|
|
||||||
DebugAssert(glCheckFramebufferStatus(target) == GL_FRAMEBUFFER_COMPLETE);
|
|
||||||
glReadPixels(xoffset, yoffset, width, height, format, type, pixels);
|
|
||||||
|
|
||||||
glBindFramebuffer(target, old_read_fbo);
|
|
||||||
glDeleteFramebuffers(1, &temp_fbo);
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "../gpu_texture.h"
|
|
||||||
#include "loader.h"
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace GL {
|
|
||||||
|
|
||||||
class Texture final : public GPUTexture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Texture();
|
|
||||||
Texture(Texture&& moved);
|
|
||||||
~Texture();
|
|
||||||
|
|
||||||
static bool UseTextureStorage(bool multisampled);
|
|
||||||
static const std::tuple<GLenum, GLenum, GLenum>& GetPixelFormatMapping(Format format);
|
|
||||||
|
|
||||||
ALWAYS_INLINE GLuint GetGLId() const { return m_id; }
|
|
||||||
bool IsValid() const override { return m_id != 0; }
|
|
||||||
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
|
|
||||||
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
|
||||||
void Unmap() override;
|
|
||||||
|
|
||||||
bool Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Format format, const void* data = nullptr,
|
|
||||||
u32 data_pitch = 0, bool linear = true, bool wrap = true);
|
|
||||||
void Destroy();
|
|
||||||
|
|
||||||
void Replace(u32 width, u32 height, GLenum internal_format, GLenum format, GLenum type, const void* data);
|
|
||||||
void ReplaceImage(u32 layer, u32 level, GLenum format, GLenum type, const void* data);
|
|
||||||
void ReplaceSubImage(u32 layer, u32 level, u32 x, u32 y, u32 width, u32 height, GLenum format, GLenum type,
|
|
||||||
const void* data);
|
|
||||||
bool CreateFramebuffer();
|
|
||||||
|
|
||||||
bool UseTextureStorage() const;
|
|
||||||
|
|
||||||
void SetLinearFilter(bool enabled) const;
|
|
||||||
void SetWrap(bool enabled) const;
|
|
||||||
|
|
||||||
ALWAYS_INLINE GLuint GetGLFramebufferID() const { return m_fbo_id; }
|
|
||||||
ALWAYS_INLINE GLenum GetGLTarget() const
|
|
||||||
{
|
|
||||||
return (IsMultisampled() ? (IsTextureArray() ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D_MULTISAMPLE_ARRAY) :
|
|
||||||
(IsTextureArray() ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bind() const;
|
|
||||||
void BindFramebuffer(GLenum target = GL_DRAW_FRAMEBUFFER) const;
|
|
||||||
void Unbind() const;
|
|
||||||
|
|
||||||
Texture& operator=(const Texture& copy) = delete;
|
|
||||||
Texture& operator=(Texture&& moved);
|
|
||||||
|
|
||||||
// Helper which uses glGetTextureSubImage where available, otherwise a temporary FBO.
|
|
||||||
static void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
|
||||||
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
|
|
||||||
GLsizei bufSize, void* pixels);
|
|
||||||
|
|
||||||
private:
|
|
||||||
GLuint m_id = 0;
|
|
||||||
GLuint m_fbo_id = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace GL
|
|
|
@ -188,7 +188,7 @@ GPUDevice::~GPUDevice()
|
||||||
|
|
||||||
RenderAPI GPUDevice::GetPreferredAPI()
|
RenderAPI GPUDevice::GetPreferredAPI()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32___ // TODO remove me
|
||||||
return RenderAPI::D3D11;
|
return RenderAPI::D3D11;
|
||||||
#else
|
#else
|
||||||
return RenderAPI::OpenGL;
|
return RenderAPI::OpenGL;
|
||||||
|
@ -508,6 +508,19 @@ void GPUDevice::DrawIndexed(u32 base_index, u32 index_count, u32 base_vertex)
|
||||||
UnreachableCode();
|
UnreachableCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GPUDevice::BeginPresent(bool skip_present)
|
||||||
|
{
|
||||||
|
// TODO: REMOVE ME
|
||||||
|
UnreachableCode();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPUDevice::EndPresent()
|
||||||
|
{
|
||||||
|
// TODO: REMOVE ME
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
|
||||||
void GPUDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
|
void GPUDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
|
||||||
u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height)
|
u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height)
|
||||||
{
|
{
|
||||||
|
@ -877,6 +890,48 @@ bool GPUDevice::IsUsingLinearFiltering() const
|
||||||
return g_settings.display_linear_filtering;
|
return g_settings.display_linear_filtering;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GPUDevice::Render(bool skip_present)
|
||||||
|
{
|
||||||
|
if (skip_present)
|
||||||
|
{
|
||||||
|
// Should never return true here..
|
||||||
|
if (UNLIKELY(BeginPresent(skip_present)))
|
||||||
|
Panic("BeginPresent() returned true when skipping...");
|
||||||
|
|
||||||
|
// Need to kick ImGui state.
|
||||||
|
ImGui::Render();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool render_frame;
|
||||||
|
if (HasDisplayTexture())
|
||||||
|
{
|
||||||
|
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||||
|
render_frame = RenderDisplay(nullptr, left, top, width, height, m_display_texture, m_display_texture_view_x,
|
||||||
|
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
|
||||||
|
IsUsingLinearFiltering());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
render_frame = BeginPresent(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!render_frame)
|
||||||
|
{
|
||||||
|
// Window minimized etc.
|
||||||
|
ImGui::Render();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetViewportAndScissor(0, 0, GetWindowWidth(), GetWindowHeight());
|
||||||
|
|
||||||
|
RenderImGui();
|
||||||
|
RenderSoftwareCursor();
|
||||||
|
|
||||||
|
EndPresent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GPUDevice::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
|
bool GPUDevice::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
|
||||||
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
|
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
|
||||||
{
|
{
|
||||||
|
@ -909,10 +964,12 @@ bool GPUDevice::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
bool GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
||||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||||
bool linear_filter)
|
bool linear_filter)
|
||||||
{
|
{
|
||||||
|
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", left, top, width, height);
|
||||||
|
|
||||||
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8; // TODO FIXME m_window_info.surface_format
|
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8; // TODO FIXME m_window_info.surface_format
|
||||||
const u32 target_width = target ? target->GetWidth() : m_window_info.surface_width;
|
const u32 target_width = target ? target->GetWidth() : m_window_info.surface_width;
|
||||||
const u32 target_height = target ? target->GetHeight() : m_window_info.surface_height;
|
const u32 target_height = target ? target->GetHeight() : m_window_info.surface_height;
|
||||||
|
@ -925,7 +982,10 @@ void GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 wid
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetFramebuffer(target);
|
if (target)
|
||||||
|
SetFramebuffer(target);
|
||||||
|
else if (!BeginPresent(false))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetPipeline(m_display_pipeline.get());
|
SetPipeline(m_display_pipeline.get());
|
||||||
|
@ -946,8 +1006,12 @@ void GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 wid
|
||||||
|
|
||||||
if (postfx)
|
if (postfx)
|
||||||
{
|
{
|
||||||
m_post_processing_chain->Apply(target, left, top, width, height, texture_view_width, texture_view_height,
|
return m_post_processing_chain->Apply(target, left, top, width, height, texture_view_width, texture_view_height,
|
||||||
target_width, target_height);
|
target_width, target_height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,17 @@ public:
|
||||||
BitField<u64, u8, 18, 4> max_lod;
|
BitField<u64, u8, 18, 4> max_lod;
|
||||||
BitField<u64, u32, 32, 32> border_color;
|
BitField<u64, u32, 32, 32> border_color;
|
||||||
u64 key;
|
u64 key;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
ALWAYS_INLINE float GetBorderRed() const { return static_cast<float>(border_color.GetValue() & 0xFF) / 255.0f; }
|
||||||
|
ALWAYS_INLINE float GetBorderGreen() const { return static_cast<float>((border_color.GetValue() >> 8) & 0xFF) / 255.0f; }
|
||||||
|
ALWAYS_INLINE float GetBorderBlue() const { return static_cast<float>((border_color.GetValue() >> 16) & 0xFF) / 255.0f; }
|
||||||
|
ALWAYS_INLINE float GetBorderAlpha() const { return static_cast<float>((border_color.GetValue() >> 24) & 0xFF) / 255.0f; }
|
||||||
|
// clang-format on
|
||||||
|
ALWAYS_INLINE std::array<float, 4> GetBorderFloatColor() const
|
||||||
|
{
|
||||||
|
return std::array<float, 4>{GetBorderRed(), GetBorderGreen(), GetBorderBlue(), GetBorderAlpha()};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
GPUSampler();
|
GPUSampler();
|
||||||
|
@ -98,7 +109,9 @@ enum class GPUShaderStage : u8
|
||||||
{
|
{
|
||||||
Vertex,
|
Vertex,
|
||||||
Fragment,
|
Fragment,
|
||||||
Compute
|
Compute,
|
||||||
|
|
||||||
|
MaxCount
|
||||||
};
|
};
|
||||||
|
|
||||||
class GPUShader
|
class GPUShader
|
||||||
|
@ -314,6 +327,17 @@ public:
|
||||||
ALWAYS_INLINE bool operator<(const BlendState& rhs) const { return key < rhs.key; }
|
ALWAYS_INLINE bool operator<(const BlendState& rhs) const { return key < rhs.key; }
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
ALWAYS_INLINE float GetConstantRed() const { return static_cast<float>(constant.GetValue() & 0xFF) / 255.0f; }
|
||||||
|
ALWAYS_INLINE float GetConstantGreen() const { return static_cast<float>((constant.GetValue() >> 8) & 0xFF) / 255.0f; }
|
||||||
|
ALWAYS_INLINE float GetConstantBlue() const { return static_cast<float>((constant.GetValue() >> 16) & 0xFF) / 255.0f; }
|
||||||
|
ALWAYS_INLINE float GetConstantAlpha() const { return static_cast<float>((constant.GetValue() >> 24) & 0xFF) / 255.0f; }
|
||||||
|
// clang-format on
|
||||||
|
ALWAYS_INLINE std::array<float, 4> GetConstantFloatColor() const
|
||||||
|
{
|
||||||
|
return std::array<float, 4>{GetConstantRed(), GetConstantGreen(), GetConstantBlue(), GetConstantAlpha()};
|
||||||
|
}
|
||||||
|
|
||||||
static BlendState GetNoBlendingState();
|
static BlendState GetNoBlendingState();
|
||||||
static BlendState GetAlphaBlendingState();
|
static BlendState GetAlphaBlendingState();
|
||||||
};
|
};
|
||||||
|
@ -398,6 +422,8 @@ public:
|
||||||
std::vector<std::string> fullscreen_modes;
|
std::vector<std::string> fullscreen_modes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr u32 MAX_TEXTURE_SAMPLERS = 4;
|
||||||
|
|
||||||
virtual ~GPUDevice();
|
virtual ~GPUDevice();
|
||||||
|
|
||||||
/// Returns the default/preferred API for the system.
|
/// Returns the default/preferred API for the system.
|
||||||
|
@ -525,7 +551,9 @@ public:
|
||||||
virtual void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex);
|
virtual void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex);
|
||||||
|
|
||||||
/// Returns false if the window was completely occluded.
|
/// Returns false if the window was completely occluded.
|
||||||
virtual bool Render(bool skip_present) = 0;
|
virtual bool BeginPresent(bool skip_present);
|
||||||
|
virtual void EndPresent();
|
||||||
|
bool Render(bool skip_present);
|
||||||
|
|
||||||
/// Renders the display with postprocessing to the specified image.
|
/// Renders the display with postprocessing to the specified image.
|
||||||
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
|
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
|
||||||
|
@ -613,7 +641,7 @@ protected:
|
||||||
|
|
||||||
void RenderSoftwareCursor();
|
void RenderSoftwareCursor();
|
||||||
|
|
||||||
void RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
bool RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
||||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||||
bool linear_filter);
|
bool linear_filter);
|
||||||
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
|
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
|
||||||
|
|
|
@ -36,16 +36,20 @@ GPUShaderCache::~GPUShaderCache()
|
||||||
|
|
||||||
bool GPUShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
bool GPUShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
||||||
{
|
{
|
||||||
return (source_hash_low == key.source_hash_low && source_hash_high == key.source_hash_high &&
|
return (std::memcmp(this, &key, sizeof(*this)) == 0);
|
||||||
entry_point_low == key.entry_point_low && entry_point_high == key.entry_point_high &&
|
|
||||||
shader_type == key.shader_type && source_length == key.source_length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
|
bool GPUShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
|
||||||
{
|
{
|
||||||
return (source_hash_low != key.source_hash_low || source_hash_high != key.source_hash_high ||
|
return (std::memcmp(this, &key, sizeof(*this)) != 0);
|
||||||
entry_point_low != key.entry_point_low || entry_point_high != key.entry_point_high ||
|
}
|
||||||
shader_type != key.shader_type || source_length != key.source_length);
|
|
||||||
|
std::size_t GPUShaderCache::CacheIndexEntryHash::operator()(const CacheIndexKey& e) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t h = 0;
|
||||||
|
hash_combine(h, e.entry_point_low, e.entry_point_high, e.source_hash_low, e.source_hash_high, e.source_length,
|
||||||
|
e.shader_type);
|
||||||
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUShaderCache::Open(const std::string_view& base_filename)
|
bool GPUShaderCache::Open(const std::string_view& base_filename)
|
||||||
|
@ -189,12 +193,8 @@ bool GPUShaderCache::ReadExisting(const std::string& index_filename, const std::
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CacheIndexKey key{static_cast<GPUShaderStage>(entry.shader_type),
|
const CacheIndexKey key{entry.shader_type, entry.source_length, entry.source_hash_low,
|
||||||
entry.source_length,
|
entry.source_hash_high, entry.entry_point_low, entry.entry_point_high};
|
||||||
entry.source_hash_low,
|
|
||||||
entry.source_hash_high,
|
|
||||||
entry.entry_point_low,
|
|
||||||
entry.entry_point_high};
|
|
||||||
const CacheIndexData data{entry.file_offset, entry.blob_size};
|
const CacheIndexData data{entry.file_offset, entry.blob_size};
|
||||||
m_index.emplace(key, data);
|
m_index.emplace(key, data);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ GPUShaderCache::CacheIndexKey GPUShaderCache::GetCacheKey(GPUShaderStage stage,
|
||||||
};
|
};
|
||||||
|
|
||||||
CacheIndexKey key = {};
|
CacheIndexKey key = {};
|
||||||
key.shader_type = stage;
|
key.shader_type = static_cast<u32>(stage);
|
||||||
|
|
||||||
MD5Digest digest;
|
MD5Digest digest;
|
||||||
digest.Update(shader_code.data(), static_cast<u32>(shader_code.length()));
|
digest.Update(shader_code.data(), static_cast<u32>(shader_code.length()));
|
||||||
|
@ -250,7 +250,7 @@ bool GPUShaderCache::Lookup(const CacheIndexKey& key, ShaderBinary* binary)
|
||||||
std::fread(binary->data(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size)
|
std::fread(binary->data(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Read %u byte %s shader from file failed", iter->second.blob_size,
|
Log_ErrorPrintf("Read %u byte %s shader from file failed", iter->second.blob_size,
|
||||||
GPUShader::GetStageName(key.shader_type));
|
GPUShader::GetStageName(static_cast<GPUShaderStage>(key.shader_type)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,11 +280,12 @@ bool GPUShaderCache::Insert(const CacheIndexKey& key, const void* data, u32 data
|
||||||
std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 || std::fflush(m_index_file) != 0)
|
std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 || std::fflush(m_index_file) != 0)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to write %u byte %s shader blob to file", data_size,
|
Log_ErrorPrintf("Failed to write %u byte %s shader blob to file", data_size,
|
||||||
GPUShader::GetStageName(key.shader_type));
|
GPUShader::GetStageName(static_cast<GPUShaderStage>(key.shader_type)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_DevPrintf("Cached %u byte %s shader", data_size, GPUShader::GetStageName(key.shader_type));
|
Log_DevPrintf("Cached %u byte %s shader", data_size,
|
||||||
|
GPUShader::GetStageName(static_cast<GPUShaderStage>(key.shader_type)));
|
||||||
m_index.emplace(key, idata);
|
m_index.emplace(key, idata);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
|
|
||||||
struct CacheIndexKey
|
struct CacheIndexKey
|
||||||
{
|
{
|
||||||
GPUShaderStage shader_type;
|
u32 shader_type;
|
||||||
u32 source_length;
|
u32 source_length;
|
||||||
u64 source_hash_low;
|
u64 source_hash_low;
|
||||||
u64 source_hash_high;
|
u64 source_hash_high;
|
||||||
|
@ -29,6 +29,12 @@ public:
|
||||||
bool operator==(const CacheIndexKey& key) const;
|
bool operator==(const CacheIndexKey& key) const;
|
||||||
bool operator!=(const CacheIndexKey& key) const;
|
bool operator!=(const CacheIndexKey& key) const;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(CacheIndexKey) == 40);
|
||||||
|
|
||||||
|
struct CacheIndexEntryHash
|
||||||
|
{
|
||||||
|
std::size_t operator()(const CacheIndexKey& e) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
GPUShaderCache();
|
GPUShaderCache();
|
||||||
~GPUShaderCache();
|
~GPUShaderCache();
|
||||||
|
@ -46,24 +52,13 @@ public:
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CacheIndexEntryHasher
|
|
||||||
{
|
|
||||||
std::size_t operator()(const CacheIndexKey& e) const noexcept
|
|
||||||
{
|
|
||||||
std::size_t h = 0;
|
|
||||||
hash_combine(h, e.entry_point_low, e.entry_point_high, e.source_hash_low, e.source_hash_high, e.source_length,
|
|
||||||
e.shader_type);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CacheIndexData
|
struct CacheIndexData
|
||||||
{
|
{
|
||||||
u32 file_offset;
|
u32 file_offset;
|
||||||
u32 blob_size;
|
u32 blob_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
|
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHash>;
|
||||||
|
|
||||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class GPUTexture
|
class GPUTexture
|
||||||
|
@ -82,6 +85,7 @@ public:
|
||||||
ALWAYS_INLINE bool IsDepthStencil() const { return (m_type == Type::DepthStencil); }
|
ALWAYS_INLINE bool IsDepthStencil() const { return (m_type == Type::DepthStencil); }
|
||||||
ALWAYS_INLINE bool IsTexture() const { return (m_type == Type::Texture); }
|
ALWAYS_INLINE bool IsTexture() const { return (m_type == Type::Texture); }
|
||||||
|
|
||||||
|
ALWAYS_INLINE const ClearValue& GetClearValue() const { return m_clear_value; }
|
||||||
ALWAYS_INLINE u32 GetClearColor() const { return m_clear_value.color; }
|
ALWAYS_INLINE u32 GetClearColor() const { return m_clear_value.color; }
|
||||||
ALWAYS_INLINE float GetClearDepth() const { return m_clear_value.depth; }
|
ALWAYS_INLINE float GetClearDepth() const { return m_clear_value.depth; }
|
||||||
ALWAYS_INLINE std::array<float, 4> GetUNormClearColor() const
|
ALWAYS_INLINE std::array<float, 4> GetUNormClearColor() const
|
||||||
|
@ -120,6 +124,8 @@ public:
|
||||||
// Instructs the backend that we're finished rendering to this texture. It may transition it to a new layout.
|
// Instructs the backend that we're finished rendering to this texture. It may transition it to a new layout.
|
||||||
virtual void MakeReadyForSampling();
|
virtual void MakeReadyForSampling();
|
||||||
|
|
||||||
|
virtual void SetDebugName(const std::string_view& name) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GPUTexture();
|
GPUTexture();
|
||||||
GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Format format);
|
GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Format format);
|
||||||
|
|
|
@ -1,569 +0,0 @@
|
||||||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
|
||||||
// - Desktop GL: 2.x 3.x 4.x
|
|
||||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
|
||||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
|
||||||
|
|
||||||
// Implemented features:
|
|
||||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
|
||||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
|
||||||
|
|
||||||
// CHANGELOG
|
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
|
||||||
// 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.
|
|
||||||
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
|
|
||||||
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
|
|
||||||
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
|
|
||||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
|
||||||
// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.
|
|
||||||
// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.
|
|
||||||
// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version.
|
|
||||||
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
|
||||||
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
|
|
||||||
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
|
|
||||||
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
|
|
||||||
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
|
|
||||||
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
|
|
||||||
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
|
|
||||||
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
|
|
||||||
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
|
|
||||||
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
|
|
||||||
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
|
|
||||||
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
|
|
||||||
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
|
|
||||||
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
|
|
||||||
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
|
|
||||||
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
|
|
||||||
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
|
||||||
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
|
||||||
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
|
|
||||||
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
|
|
||||||
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
|
|
||||||
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
|
|
||||||
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
|
|
||||||
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
|
|
||||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
|
||||||
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
|
|
||||||
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
|
|
||||||
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
|
|
||||||
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
|
|
||||||
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
|
|
||||||
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
|
|
||||||
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
|
||||||
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
|
|
||||||
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
|
|
||||||
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
|
|
||||||
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
|
|
||||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
|
|
||||||
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
|
|
||||||
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
|
|
||||||
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
|
|
||||||
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
|
|
||||||
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
|
|
||||||
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
|
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
// OpenGL GLSL GLSL
|
|
||||||
// version version string
|
|
||||||
//----------------------------------------
|
|
||||||
// 2.0 110 "#version 110"
|
|
||||||
// 2.1 120 "#version 120"
|
|
||||||
// 3.0 130 "#version 130"
|
|
||||||
// 3.1 140 "#version 140"
|
|
||||||
// 3.2 150 "#version 150"
|
|
||||||
// 3.3 330 "#version 330 core"
|
|
||||||
// 4.0 400 "#version 400 core"
|
|
||||||
// 4.1 410 "#version 410 core"
|
|
||||||
// 4.2 420 "#version 410 core"
|
|
||||||
// 4.3 430 "#version 430 core"
|
|
||||||
// ES 2.0 100 "#version 100" = WebGL 1.0
|
|
||||||
// ES 3.0 300 "#version 300 es" = WebGL 2.0
|
|
||||||
//----------------------------------------
|
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_impl_opengl3.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
|
||||||
#include <stddef.h> // intptr_t
|
|
||||||
#else
|
|
||||||
#include <stdint.h> // intptr_t
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Clang warnings with -Weverything
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
|
|
||||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
|
||||||
#if __has_warning("-Wzero-as-null-pointer-constant")
|
|
||||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// GL includes
|
|
||||||
#include "gl/loader.h"
|
|
||||||
#include "gl/texture.h"
|
|
||||||
#include "common/log.h"
|
|
||||||
Log_SetChannel(ImGui_ImplOpenGL3);
|
|
||||||
|
|
||||||
// OpenGL Data
|
|
||||||
struct ImGui_ImplOpenGL3_Data
|
|
||||||
{
|
|
||||||
GLuint GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
|
||||||
char GlslVersionString[32] = {}; // Specified by user or detected based on compile time GL settings.
|
|
||||||
GL::Texture FontTexture;
|
|
||||||
GLuint ShaderHandle = 0;
|
|
||||||
GLint AttribLocationTex = 0; // Uniforms location
|
|
||||||
GLint AttribLocationProjMtx = 0;
|
|
||||||
GLuint AttribLocationVtxPos = 0; // Vertex attributes location
|
|
||||||
GLuint AttribLocationVtxUV = 0;
|
|
||||||
GLuint AttribLocationVtxColor = 0;
|
|
||||||
unsigned int VboHandle = 0, ElementsHandle = 0, VaoHandle = 0;
|
|
||||||
GLsizeiptr VertexBufferSize = 0;
|
|
||||||
GLsizeiptr IndexBufferSize = 0;
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_Data() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
|
||||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
|
||||||
static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
|
|
||||||
{
|
|
||||||
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
|
||||||
{
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
|
||||||
|
|
||||||
// Setup backend capabilities flags
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
|
|
||||||
io.BackendRendererUserData = (void*)bd;
|
|
||||||
io.BackendRendererName = "imgui_impl_opengl3";
|
|
||||||
|
|
||||||
// Query for GL version (e.g. 320 for GL 3.2)
|
|
||||||
GLint major = 0;
|
|
||||||
GLint minor = 0;
|
|
||||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
||||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
||||||
if (major == 0 && minor == 0)
|
|
||||||
{
|
|
||||||
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
|
||||||
const char* gl_version = (const char*)glGetString(GL_VERSION);
|
|
||||||
sscanf(gl_version, "%d.%d", &major, &minor);
|
|
||||||
}
|
|
||||||
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
|
|
||||||
|
|
||||||
// Store GLSL version string so we can refer to it later in case we recreate shaders.
|
|
||||||
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
|
||||||
if (glsl_version == NULL)
|
|
||||||
glsl_version = "#version 130";
|
|
||||||
|
|
||||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
|
|
||||||
strcpy(bd->GlslVersionString, glsl_version);
|
|
||||||
strcat(bd->GlslVersionString, "\n");
|
|
||||||
|
|
||||||
if (glDrawElementsBaseVertex)
|
|
||||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
|
||||||
else
|
|
||||||
Log_WarningPrintf("Missing glDrawElementsBaseVertex()");
|
|
||||||
|
|
||||||
return ImGui_ImplOpenGL3_CreateDeviceObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGui_ImplOpenGL3_Shutdown()
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
|
||||||
io.BackendRendererName = NULL;
|
|
||||||
io.BackendRendererUserData = NULL;
|
|
||||||
IM_DELETE(bd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
|
|
||||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendEquation(GL_FUNC_ADD);
|
|
||||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
glDisable(GL_STENCIL_TEST);
|
|
||||||
glEnable(GL_SCISSOR_TEST);
|
|
||||||
if (glPolygonMode)
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
||||||
|
|
||||||
// Setup viewport, orthographic projection matrix
|
|
||||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
|
||||||
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
|
||||||
float L = draw_data->DisplayPos.x;
|
|
||||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
|
||||||
float T = draw_data->DisplayPos.y;
|
|
||||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
|
||||||
const float ortho_projection[4][4] =
|
|
||||||
{
|
|
||||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
|
||||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
|
||||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
|
||||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
|
||||||
};
|
|
||||||
glUseProgram(bd->ShaderHandle);
|
|
||||||
glUniform1i(bd->AttribLocationTex, 0);
|
|
||||||
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
|
||||||
|
|
||||||
// Bind vertex/index buffers and setup attributes for ImDrawVert
|
|
||||||
if (bd->VaoHandle)
|
|
||||||
glBindVertexArray(bd->VaoHandle);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle);
|
|
||||||
glEnableVertexAttribArray(bd->AttribLocationVtxPos);
|
|
||||||
glEnableVertexAttribArray(bd->AttribLocationVtxUV);
|
|
||||||
glEnableVertexAttribArray(bd->AttribLocationVtxColor);
|
|
||||||
glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
|
||||||
glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
|
||||||
glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenGL3 Render function.
|
|
||||||
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
|
|
||||||
// This is in order to be able to run within an OpenGL engine that doesn't do so.
|
|
||||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
|
||||||
{
|
|
||||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
|
||||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
|
||||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
|
||||||
if (fb_width <= 0 || fb_height <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
|
|
||||||
// Setup desired GL state
|
|
||||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
|
|
||||||
|
|
||||||
// Will project scissor/clipping rectangles into framebuffer space
|
|
||||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
|
||||||
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
|
||||||
|
|
||||||
// Render command lists
|
|
||||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
|
||||||
{
|
|
||||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
|
||||||
|
|
||||||
// Upload vertex/index buffers
|
|
||||||
GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
|
|
||||||
GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
|
|
||||||
if (bd->VertexBufferSize < vtx_buffer_size)
|
|
||||||
{
|
|
||||||
bd->VertexBufferSize = vtx_buffer_size;
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW);
|
|
||||||
}
|
|
||||||
if (bd->IndexBufferSize < idx_buffer_size)
|
|
||||||
{
|
|
||||||
bd->IndexBufferSize = idx_buffer_size;
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW);
|
|
||||||
}
|
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data);
|
|
||||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data);
|
|
||||||
|
|
||||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
|
||||||
{
|
|
||||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
|
||||||
if (pcmd->UserCallback != NULL)
|
|
||||||
{
|
|
||||||
// User callback, registered via ImDrawList::AddCallback()
|
|
||||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
|
||||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
|
||||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
|
|
||||||
else
|
|
||||||
pcmd->UserCallback(cmd_list, pcmd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Project scissor/clipping rectangles into framebuffer space
|
|
||||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
|
||||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
|
||||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
|
|
||||||
glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
|
|
||||||
|
|
||||||
// Bind texture, Draw
|
|
||||||
const GL::Texture* tex = static_cast<const GL::Texture*>(pcmd->GetTexID());
|
|
||||||
if (tex)
|
|
||||||
tex->Bind();
|
|
||||||
|
|
||||||
if (glDrawElementsBaseVertex)
|
|
||||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
|
||||||
else
|
|
||||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bd->VaoHandle)
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
|
||||||
{
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
|
|
||||||
// Build texture atlas
|
|
||||||
unsigned char* pixels;
|
|
||||||
int width, height;
|
|
||||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
|
||||||
|
|
||||||
// Upload texture to graphics system
|
|
||||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
|
||||||
bd->FontTexture.Create(width, height, 1, 1, 1, GPUTexture::Format::RGBA8, pixels);
|
|
||||||
bd->FontTexture.SetLinearFilter(true);
|
|
||||||
|
|
||||||
// Store our identifier
|
|
||||||
io.Fonts->SetTexID(&bd->FontTexture);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
if (bd->FontTexture.IsValid())
|
|
||||||
bd->FontTexture.Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
|
||||||
static bool CheckShader(GLuint handle, const char* desc)
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
GLint status = 0, log_length = 0;
|
|
||||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
|
||||||
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
|
||||||
if ((GLboolean)status == GL_FALSE)
|
|
||||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString);
|
|
||||||
if (log_length > 1)
|
|
||||||
{
|
|
||||||
ImVector<char> buf;
|
|
||||||
buf.resize((int)(log_length + 1));
|
|
||||||
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
|
||||||
fprintf(stderr, "%s\n", buf.begin());
|
|
||||||
}
|
|
||||||
return (GLboolean)status == GL_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
|
||||||
static bool CheckProgram(GLuint handle, const char* desc)
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
GLint status = 0, log_length = 0;
|
|
||||||
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
|
||||||
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
|
||||||
if ((GLboolean)status == GL_FALSE)
|
|
||||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString);
|
|
||||||
if (log_length > 1)
|
|
||||||
{
|
|
||||||
ImVector<char> buf;
|
|
||||||
buf.resize((int)(log_length + 1));
|
|
||||||
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
|
||||||
fprintf(stderr, "%s\n", buf.begin());
|
|
||||||
}
|
|
||||||
return (GLboolean)status == GL_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
|
|
||||||
// Parse GLSL version string
|
|
||||||
int glsl_version = 130;
|
|
||||||
sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
|
|
||||||
|
|
||||||
const GLchar* vertex_shader_glsl_120 =
|
|
||||||
"uniform mat4 ProjMtx;\n"
|
|
||||||
"attribute vec2 Position;\n"
|
|
||||||
"attribute vec2 UV;\n"
|
|
||||||
"attribute vec4 Color;\n"
|
|
||||||
"varying vec2 Frag_UV;\n"
|
|
||||||
"varying vec4 Frag_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Frag_UV = UV;\n"
|
|
||||||
" Frag_Color = Color;\n"
|
|
||||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* vertex_shader_glsl_130 =
|
|
||||||
"uniform mat4 ProjMtx;\n"
|
|
||||||
"in vec2 Position;\n"
|
|
||||||
"in vec2 UV;\n"
|
|
||||||
"in vec4 Color;\n"
|
|
||||||
"out vec2 Frag_UV;\n"
|
|
||||||
"out vec4 Frag_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Frag_UV = UV;\n"
|
|
||||||
" Frag_Color = Color;\n"
|
|
||||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* vertex_shader_glsl_300_es =
|
|
||||||
"precision highp float;\n"
|
|
||||||
"layout (location = 0) in vec2 Position;\n"
|
|
||||||
"layout (location = 1) in vec2 UV;\n"
|
|
||||||
"layout (location = 2) in vec4 Color;\n"
|
|
||||||
"uniform mat4 ProjMtx;\n"
|
|
||||||
"out vec2 Frag_UV;\n"
|
|
||||||
"out vec4 Frag_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Frag_UV = UV;\n"
|
|
||||||
" Frag_Color = Color;\n"
|
|
||||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* vertex_shader_glsl_410_core =
|
|
||||||
"layout (location = 0) in vec2 Position;\n"
|
|
||||||
"layout (location = 1) in vec2 UV;\n"
|
|
||||||
"layout (location = 2) in vec4 Color;\n"
|
|
||||||
"uniform mat4 ProjMtx;\n"
|
|
||||||
"out vec2 Frag_UV;\n"
|
|
||||||
"out vec4 Frag_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Frag_UV = UV;\n"
|
|
||||||
" Frag_Color = Color;\n"
|
|
||||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* fragment_shader_glsl_120 =
|
|
||||||
"#ifdef GL_ES\n"
|
|
||||||
" precision mediump float;\n"
|
|
||||||
"#endif\n"
|
|
||||||
"uniform sampler2D Texture;\n"
|
|
||||||
"varying vec2 Frag_UV;\n"
|
|
||||||
"varying vec4 Frag_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* fragment_shader_glsl_130 =
|
|
||||||
"uniform sampler2D Texture;\n"
|
|
||||||
"in vec2 Frag_UV;\n"
|
|
||||||
"in vec4 Frag_Color;\n"
|
|
||||||
"out vec4 Out_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* fragment_shader_glsl_300_es =
|
|
||||||
"precision mediump float;\n"
|
|
||||||
"uniform sampler2D Texture;\n"
|
|
||||||
"in vec2 Frag_UV;\n"
|
|
||||||
"in vec4 Frag_Color;\n"
|
|
||||||
"layout (location = 0) out vec4 Out_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar* fragment_shader_glsl_410_core =
|
|
||||||
"in vec2 Frag_UV;\n"
|
|
||||||
"in vec4 Frag_Color;\n"
|
|
||||||
"uniform sampler2D Texture;\n"
|
|
||||||
"layout (location = 0) out vec4 Out_Color;\n"
|
|
||||||
"void main()\n"
|
|
||||||
"{\n"
|
|
||||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
// Select shaders matching our GLSL versions
|
|
||||||
const GLchar* vertex_shader = NULL;
|
|
||||||
const GLchar* fragment_shader = NULL;
|
|
||||||
if (glsl_version < 130)
|
|
||||||
{
|
|
||||||
vertex_shader = vertex_shader_glsl_120;
|
|
||||||
fragment_shader = fragment_shader_glsl_120;
|
|
||||||
}
|
|
||||||
else if (glsl_version >= 410)
|
|
||||||
{
|
|
||||||
vertex_shader = vertex_shader_glsl_410_core;
|
|
||||||
fragment_shader = fragment_shader_glsl_410_core;
|
|
||||||
}
|
|
||||||
else if (glsl_version == 300)
|
|
||||||
{
|
|
||||||
vertex_shader = vertex_shader_glsl_300_es;
|
|
||||||
fragment_shader = fragment_shader_glsl_300_es;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vertex_shader = vertex_shader_glsl_130;
|
|
||||||
fragment_shader = fragment_shader_glsl_130;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create shaders
|
|
||||||
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
|
|
||||||
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL);
|
|
||||||
glCompileShader(vert_handle);
|
|
||||||
CheckShader(vert_handle, "vertex shader");
|
|
||||||
|
|
||||||
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
|
|
||||||
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL);
|
|
||||||
glCompileShader(frag_handle);
|
|
||||||
CheckShader(frag_handle, "fragment shader");
|
|
||||||
|
|
||||||
// Link
|
|
||||||
bd->ShaderHandle = glCreateProgram();
|
|
||||||
glAttachShader(bd->ShaderHandle, vert_handle);
|
|
||||||
glAttachShader(bd->ShaderHandle, frag_handle);
|
|
||||||
glLinkProgram(bd->ShaderHandle);
|
|
||||||
CheckProgram(bd->ShaderHandle, "shader program");
|
|
||||||
|
|
||||||
glDetachShader(bd->ShaderHandle, vert_handle);
|
|
||||||
glDetachShader(bd->ShaderHandle, frag_handle);
|
|
||||||
glDeleteShader(vert_handle);
|
|
||||||
glDeleteShader(frag_handle);
|
|
||||||
|
|
||||||
bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
|
|
||||||
bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
|
|
||||||
bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
|
|
||||||
bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
|
|
||||||
bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
|
|
||||||
|
|
||||||
// Create buffers
|
|
||||||
glGenBuffers(1, &bd->VboHandle);
|
|
||||||
glGenBuffers(1, &bd->ElementsHandle);
|
|
||||||
|
|
||||||
if (glGenVertexArrays)
|
|
||||||
glGenVertexArrays(1, &bd->VaoHandle);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
|
||||||
{
|
|
||||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
|
||||||
if (bd->VaoHandle) { glDeleteVertexArrays(1, &bd->VaoHandle); bd->VaoHandle = 0; }
|
|
||||||
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
|
|
||||||
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
|
|
||||||
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
|
|
||||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
|
@ -1,16 +0,0 @@
|
||||||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
|
||||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "imgui.h" // IMGUI_IMPL_API
|
|
||||||
|
|
||||||
// Backend API
|
|
||||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
|
|
||||||
void ImGui_ImplOpenGL3_Shutdown();
|
|
||||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
|
||||||
|
|
||||||
// (Optional) Called by Init/NewFrame/Shutdown
|
|
||||||
bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
|
||||||
void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
|
||||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
|
||||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
|
|
@ -0,0 +1,949 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#include "opengl_device.h"
|
||||||
|
#include "../settings.h"
|
||||||
|
#include "opengl_pipeline.h"
|
||||||
|
#include "opengl_stream_buffer.h"
|
||||||
|
#include "opengl_texture.h"
|
||||||
|
#include "postprocessing_shadergen.h"
|
||||||
|
|
||||||
|
#include "common/align.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
Log_SetChannel(OpenGLDevice);
|
||||||
|
|
||||||
|
OpenGLDevice::OpenGLDevice()
|
||||||
|
{
|
||||||
|
// Something which won't be matched..
|
||||||
|
std::memset(&m_last_rasterization_state, 0xFF, sizeof(m_last_rasterization_state));
|
||||||
|
std::memset(&m_last_depth_state, 0xFF, sizeof(m_last_depth_state));
|
||||||
|
std::memset(&m_last_blend_state, 0xFF, sizeof(m_last_blend_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLDevice::~OpenGLDevice()
|
||||||
|
{
|
||||||
|
// TODO: Destroy() function
|
||||||
|
if (!m_gl_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DestroyResources();
|
||||||
|
DestroyBuffers();
|
||||||
|
|
||||||
|
m_gl_context->DoneCurrent();
|
||||||
|
m_gl_context.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::BindUpdateTextureUnit()
|
||||||
|
{
|
||||||
|
GetInstance().SetActiveTexture(UPDATE_TEXTURE_UNIT - GL_TEXTURE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderAPI OpenGLDevice::GetRenderAPI() const
|
||||||
|
{
|
||||||
|
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
|
const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||||
|
{
|
||||||
|
std::unique_ptr<OpenGLTexture> tex(std::make_unique<OpenGLTexture>());
|
||||||
|
if (!tex->Create(width, height, layers, levels, samples, format, data, data_stride))
|
||||||
|
tex.reset();
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
|
||||||
|
u32 out_data_stride)
|
||||||
|
{
|
||||||
|
OpenGLTexture* T = static_cast<OpenGLTexture*>(texture);
|
||||||
|
|
||||||
|
GLint alignment;
|
||||||
|
if (out_data_stride & 1)
|
||||||
|
alignment = 1;
|
||||||
|
else if (out_data_stride & 2)
|
||||||
|
alignment = 2;
|
||||||
|
else
|
||||||
|
alignment = 4;
|
||||||
|
|
||||||
|
glPixelStorei(GL_PACK_ALIGNMENT, alignment);
|
||||||
|
glPixelStorei(GL_PACK_ROW_LENGTH, out_data_stride / T->GetPixelSize());
|
||||||
|
|
||||||
|
const auto [gl_internal_format, gl_format, gl_type] = OpenGLTexture::GetPixelFormatMapping(T->GetFormat());
|
||||||
|
const u32 layer = 0;
|
||||||
|
const u32 level = 0;
|
||||||
|
|
||||||
|
if (GLAD_GL_VERSION_4_5 || GLAD_GL_ARB_get_texture_sub_image)
|
||||||
|
{
|
||||||
|
glGetTextureSubImage(T->GetGLId(), level, x, y, layer, width, height, 1, gl_format, gl_type,
|
||||||
|
height * out_data_stride, out_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (T->GetLayers() > 0)
|
||||||
|
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, T->GetGLId(), level, layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, T->GetGLId(), level);
|
||||||
|
|
||||||
|
DebugAssert(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||||
|
glReadPixels(x, y, width, height, gl_format, gl_type, out_data);
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::SupportsTextureFormat(GPUTexture::Format format) const
|
||||||
|
{
|
||||||
|
const auto [gl_internal_format, gl_format, gl_type] = OpenGLTexture::GetPixelFormatMapping(format);
|
||||||
|
return (gl_internal_format != static_cast<GLenum>(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
|
||||||
|
GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width,
|
||||||
|
u32 height)
|
||||||
|
{
|
||||||
|
OpenGLTexture* D = static_cast<OpenGLTexture*>(dst);
|
||||||
|
OpenGLTexture* S = static_cast<OpenGLTexture*>(src);
|
||||||
|
CommitClear(D);
|
||||||
|
CommitClear(S);
|
||||||
|
|
||||||
|
const GLuint sid = S->GetGLId();
|
||||||
|
const GLuint did = D->GetGLId();
|
||||||
|
if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image)
|
||||||
|
{
|
||||||
|
glCopyImageSubData(sid, GL_TEXTURE_2D, src_level, src_x, src_y, src_layer, did, GL_TEXTURE_2D, dst_level, dst_x,
|
||||||
|
dst_y, dst_layer, width, height, 1);
|
||||||
|
}
|
||||||
|
else if (GLAD_GL_EXT_copy_image)
|
||||||
|
{
|
||||||
|
glCopyImageSubDataEXT(sid, GL_TEXTURE_2D, src_level, src_x, src_y, src_layer, did, GL_TEXTURE_2D, dst_level, dst_x,
|
||||||
|
dst_y, dst_layer, width, height, 1);
|
||||||
|
}
|
||||||
|
else if (GLAD_GL_OES_copy_image)
|
||||||
|
{
|
||||||
|
glCopyImageSubDataOES(sid, GL_TEXTURE_2D, src_level, src_x, src_y, src_layer, did, GL_TEXTURE_2D, dst_level, dst_x,
|
||||||
|
dst_y, dst_layer, width, height, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_read_fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_write_fbo);
|
||||||
|
if (D->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, did, dst_level, dst_layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, did, dst_level);
|
||||||
|
if (S->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, sid, src_level, src_layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sid, src_level);
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBlitFramebuffer(src_x, src_y, src_x + width, src_y + width, dst_x, dst_y, dst_x + width, dst_y + height,
|
||||||
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_current_framebuffer ? m_current_framebuffer->GetGLId() : 0);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
|
||||||
|
GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width,
|
||||||
|
u32 height)
|
||||||
|
{
|
||||||
|
OpenGLTexture* D = static_cast<OpenGLTexture*>(dst);
|
||||||
|
OpenGLTexture* S = static_cast<OpenGLTexture*>(src);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_read_fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_write_fbo);
|
||||||
|
if (D->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, D->GetGLId(), dst_level, dst_layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, D->GetGLId(), dst_level);
|
||||||
|
if (S->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, S->GetGLId(), src_level, src_layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, S->GetGLId(), src_level);
|
||||||
|
|
||||||
|
CommitClear(S);
|
||||||
|
if (width == D->GetMipWidth(dst_level) && height == D->GetMipHeight(dst_level))
|
||||||
|
{
|
||||||
|
D->SetState(GPUTexture::State::Dirty);
|
||||||
|
if (glInvalidateFramebuffer)
|
||||||
|
{
|
||||||
|
const GLenum attachment = GL_COLOR_ATTACHMENT0;
|
||||||
|
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CommitClear(D);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBlitFramebuffer(src_x, src_y, src_x + width, src_y + width, dst_x, dst_y, dst_x + width, dst_y + height,
|
||||||
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_current_framebuffer ? m_current_framebuffer->GetGLId() : 0);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::PushDebugGroup(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (!glPushDebugGroup || !g_settings.gpu_use_debug_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap));
|
||||||
|
va_end(ap);
|
||||||
|
if (!buf.empty())
|
||||||
|
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, static_cast<GLsizei>(buf.size()), buf.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::PopDebugGroup()
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (!glPopDebugGroup || !g_settings.gpu_use_debug_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
glPopDebugGroup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::InsertDebugMessage(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (!glDebugMessageInsert || !g_settings.gpu_use_debug_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap));
|
||||||
|
va_end(ap);
|
||||||
|
if (!buf.empty())
|
||||||
|
{
|
||||||
|
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0, GL_DEBUG_SEVERITY_NOTIFICATION,
|
||||||
|
static_cast<GLsizei>(buf.size()), buf.c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetVSync(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_vsync_enabled == enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_vsync_enabled = enabled;
|
||||||
|
SetSwapInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||||
|
const GLchar* message, const void* userParam)
|
||||||
|
{
|
||||||
|
switch (severity)
|
||||||
|
{
|
||||||
|
case GL_DEBUG_SEVERITY_HIGH_KHR:
|
||||||
|
Log_ErrorPrint(message);
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SEVERITY_MEDIUM_KHR:
|
||||||
|
Log_WarningPrint(message);
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SEVERITY_LOW_KHR:
|
||||||
|
Log_InfoPrint(message);
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||||
|
// Log_DebugPrint(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::HasSurface() const
|
||||||
|
{
|
||||||
|
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::CreateDevice(const WindowInfo& wi, bool vsync)
|
||||||
|
{
|
||||||
|
m_gl_context = GL::Context::Create(wi);
|
||||||
|
if (!m_gl_context)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create any GL context");
|
||||||
|
m_gl_context.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
|
m_vsync_enabled = vsync;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::SetupDevice()
|
||||||
|
{
|
||||||
|
if (!GPUDevice::SetupDevice())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OpenGLTexture::s_use_pbo_for_uploads = true;
|
||||||
|
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||||
|
{
|
||||||
|
// Adreno seems to corrupt textures through PBOs... and Mali is slow.
|
||||||
|
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||||
|
if (std::strstr(gl_vendor, "Qualcomm") || std::strstr(gl_vendor, "ARM") || std::strstr(gl_vendor, "Broadcom"))
|
||||||
|
OpenGLTexture::s_use_pbo_for_uploads = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_VerbosePrintf("Using PBO for uploads: %s", OpenGLTexture::s_use_pbo_for_uploads ? "yes" : "no");
|
||||||
|
|
||||||
|
if (g_settings.gpu_use_debug_device && GLAD_GL_KHR_debug)
|
||||||
|
{
|
||||||
|
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||||
|
glDebugMessageCallbackKHR(GLDebugCallback, nullptr);
|
||||||
|
else
|
||||||
|
glDebugMessageCallback(GLDebugCallback, nullptr);
|
||||||
|
|
||||||
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckFeatures())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!CreateBuffers() || !CreateResources())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::CheckFeatures()
|
||||||
|
{
|
||||||
|
const bool is_gles = m_gl_context->IsGLES();
|
||||||
|
|
||||||
|
GLint max_texture_size = 1024;
|
||||||
|
GLint max_samples = 1;
|
||||||
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
||||||
|
glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
|
||||||
|
m_max_texture_size = std::max(1024u, static_cast<u32>(max_texture_size));
|
||||||
|
m_max_multisamples = std::max(1u, static_cast<u32>(max_samples));
|
||||||
|
|
||||||
|
GLint max_dual_source_draw_buffers = 0;
|
||||||
|
glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, &max_dual_source_draw_buffers);
|
||||||
|
m_features.dual_source_blend =
|
||||||
|
(max_dual_source_draw_buffers > 0) &&
|
||||||
|
(GLAD_GL_VERSION_3_3 || GLAD_GL_ARB_blend_func_extended || GLAD_GL_EXT_blend_func_extended);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Partial texture buffer uploads appear to be broken in macOS's OpenGL driver.
|
||||||
|
m_features.supports_texture_buffers = false;
|
||||||
|
#else
|
||||||
|
m_features.supports_texture_buffers = (GLAD_GL_VERSION_3_1 || GLAD_GL_ES_VERSION_3_2);
|
||||||
|
|
||||||
|
// And Samsung's ANGLE/GLES driver?
|
||||||
|
if (std::strstr(reinterpret_cast<const char*>(glGetString(GL_RENDERER)), "ANGLE"))
|
||||||
|
m_features.supports_texture_buffers = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!m_features.supports_texture_buffers)
|
||||||
|
{
|
||||||
|
// Try SSBOs.
|
||||||
|
GLint max_fragment_storage_blocks = 0;
|
||||||
|
GLint64 max_ssbo_size = 0;
|
||||||
|
if (GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1 || GLAD_GL_ARB_shader_storage_buffer_object)
|
||||||
|
{
|
||||||
|
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &max_fragment_storage_blocks);
|
||||||
|
glGetInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_InfoPrintf("Max fragment shader storage blocks: %d", max_fragment_storage_blocks);
|
||||||
|
Log_InfoPrintf("Max shader storage buffer size: %" PRId64, max_ssbo_size);
|
||||||
|
m_features.texture_buffers_emulated_with_ssbo =
|
||||||
|
(max_fragment_storage_blocks > 0 && max_ssbo_size >= static_cast<GLint64>(1024 * 512 * sizeof(u16)));
|
||||||
|
if (m_features.texture_buffers_emulated_with_ssbo)
|
||||||
|
{
|
||||||
|
// TODO: SSBOs should be clamped to max size.
|
||||||
|
Log_InfoPrintf("Using shader storage buffers for VRAM writes.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Both texture buffers and SSBOs are not supported.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_features.per_sample_shading = GLAD_GL_VERSION_4_0 || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_ARB_sample_shading;
|
||||||
|
|
||||||
|
// adaptive smoothing would require texture views, which aren't in GLES.
|
||||||
|
m_features.mipmapped_render_targets = false;
|
||||||
|
|
||||||
|
// noperspective is not supported in GLSL ES.
|
||||||
|
m_features.noperspective_interpolation = !is_gles;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::MakeCurrent()
|
||||||
|
{
|
||||||
|
if (!m_gl_context->MakeCurrent())
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to make GL context current");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSwapInterval();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::DoneCurrent()
|
||||||
|
{
|
||||||
|
return m_gl_context->DoneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::ChangeWindow(const WindowInfo& new_wi)
|
||||||
|
{
|
||||||
|
Assert(m_gl_context);
|
||||||
|
|
||||||
|
if (!m_gl_context->ChangeSurface(new_wi))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to change surface");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
|
|
||||||
|
// Update swap interval for new surface.
|
||||||
|
if (m_gl_context->IsCurrent())
|
||||||
|
SetSwapInterval();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
||||||
|
{
|
||||||
|
if (!m_gl_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
||||||
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetSwapInterval()
|
||||||
|
{
|
||||||
|
if (m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||||
|
const s32 interval = m_vsync_enabled ? 1 : 0;
|
||||||
|
GLint current_fbo = 0;
|
||||||
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
if (!m_gl_context->SetSwapInterval(interval))
|
||||||
|
Log_WarningPrintf("Failed to set swap interval to %d", interval);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::SupportsFullscreen() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::IsFullscreen()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUDevice::AdapterAndModeList OpenGLDevice::GetAdapterAndModeList()
|
||||||
|
{
|
||||||
|
AdapterAndModeList aml;
|
||||||
|
|
||||||
|
if (m_gl_context)
|
||||||
|
{
|
||||||
|
for (const GL::Context::FullscreenModeInfo& fmi : m_gl_context->EnumerateFullscreenModes())
|
||||||
|
{
|
||||||
|
aml.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aml;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::DestroySurface()
|
||||||
|
{
|
||||||
|
if (!m_gl_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_window_info.SetSurfaceless();
|
||||||
|
if (!m_gl_context->ChangeSurface(m_window_info))
|
||||||
|
Log_ErrorPrintf("Failed to switch to surfaceless");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OpenGLDevice::GetShaderCacheBaseName(const std::string_view& type, bool debug) const
|
||||||
|
{
|
||||||
|
return fmt::format("opengl_{}{}", type, debug ? "_debug" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::CreateBuffers()
|
||||||
|
{
|
||||||
|
if (!(m_vertex_buffer = OpenGLStreamBuffer::Create(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE)) ||
|
||||||
|
!(m_index_buffer = OpenGLStreamBuffer::Create(GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE)) ||
|
||||||
|
!(m_uniform_buffer = OpenGLStreamBuffer::Create(GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE)))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create one or more device buffers.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_vertex_buffer, "Device Vertex Buffer");
|
||||||
|
GL_OBJECT_NAME(m_index_buffer, "Device Index Buffer");
|
||||||
|
GL_OBJECT_NAME(m_uniform_buffer, "Device Uniform Buffer");
|
||||||
|
|
||||||
|
// TODO NOTE If we don't have GLES3.1, then SV_VertexID isn't defined when no VBOs are active.
|
||||||
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, reinterpret_cast<GLint*>(&m_uniform_buffer_alignment));
|
||||||
|
|
||||||
|
// TODO: buggy pbo
|
||||||
|
if (true)
|
||||||
|
{
|
||||||
|
if (!(m_texture_stream_buffer = OpenGLStreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, TEXTURE_STREAM_BUFFER_SIZE)))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create texture stream buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to unbind otherwise normal uploads will fail.
|
||||||
|
m_texture_stream_buffer->Unbind();
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_texture_stream_buffer, "Device Texture Stream Buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint fbos[2];
|
||||||
|
glGetError();
|
||||||
|
glGenFramebuffers(static_cast<GLsizei>(std::size(fbos)), fbos);
|
||||||
|
if (const GLenum err = glGetError(); err != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create framebuffers: %u", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_read_fbo = fbos[0];
|
||||||
|
m_write_fbo = fbos[1];
|
||||||
|
|
||||||
|
// Read FBO gets left bound.
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_read_fbo);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::DestroyBuffers()
|
||||||
|
{
|
||||||
|
if (m_write_fbo != 0)
|
||||||
|
glDeleteFramebuffers(1, &m_write_fbo);
|
||||||
|
if (m_read_fbo != 0)
|
||||||
|
glDeleteFramebuffers(1, &m_read_fbo);
|
||||||
|
m_texture_stream_buffer.reset();
|
||||||
|
m_uniform_buffer.reset();
|
||||||
|
m_index_buffer.reset();
|
||||||
|
m_vertex_buffer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::BeginPresent(bool skip_present)
|
||||||
|
{
|
||||||
|
if (skip_present || m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||||
|
{
|
||||||
|
if (!skip_present)
|
||||||
|
glFlush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
m_current_framebuffer = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::EndPresent()
|
||||||
|
{
|
||||||
|
DebugAssert(!m_current_framebuffer);
|
||||||
|
|
||||||
|
if (m_gpu_timing_enabled)
|
||||||
|
PopTimestampQuery();
|
||||||
|
|
||||||
|
m_gl_context->SwapBuffers();
|
||||||
|
|
||||||
|
if (m_gpu_timing_enabled)
|
||||||
|
KickTimestampQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
void OpenGLDevice::RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, OpenGLTexture* texture,
|
||||||
|
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
|
||||||
|
s32 texture_view_height, bool linear_filter)
|
||||||
|
{
|
||||||
|
glViewport(left, bottom, width, height);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
m_display_program.Bind();
|
||||||
|
texture->Bind();
|
||||||
|
|
||||||
|
const bool linear = IsUsingLinearFiltering();
|
||||||
|
|
||||||
|
if (!m_use_gles2_draw_path)
|
||||||
|
{
|
||||||
|
const float position_adjust = linear ? 0.5f : 0.0f;
|
||||||
|
const float size_adjust = linear ? 1.0f : 0.0f;
|
||||||
|
const float flip_adjust = (texture_view_height < 0) ? -1.0f : 1.0f;
|
||||||
|
m_display_program.Uniform4f(
|
||||||
|
0, (static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture->GetWidth()),
|
||||||
|
(static_cast<float>(texture_view_y) + (position_adjust * flip_adjust)) / static_cast<float>(texture->GetHeight()),
|
||||||
|
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture->GetWidth()),
|
||||||
|
(static_cast<float>(texture_view_height) - (size_adjust * flip_adjust)) /
|
||||||
|
static_cast<float>(texture->GetHeight()));
|
||||||
|
glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler);
|
||||||
|
glBindVertexArray(m_display_vao);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
glBindSampler(0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texture->SetLinearFilter(linear_filter);
|
||||||
|
|
||||||
|
DrawFullscreenQuadES2(m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||||
|
m_display_texture_view_height, texture->GetWidth(), texture->GetHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void OpenGLDevice::CreateTimestampQueries()
|
||||||
|
{
|
||||||
|
const bool gles = m_gl_context->IsGLES();
|
||||||
|
const auto GenQueries = gles ? glGenQueriesEXT : glGenQueries;
|
||||||
|
|
||||||
|
GenQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
|
||||||
|
KickTimestampQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::DestroyTimestampQueries()
|
||||||
|
{
|
||||||
|
if (m_timestamp_queries[0] == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool gles = m_gl_context->IsGLES();
|
||||||
|
const auto DeleteQueries = gles ? glDeleteQueriesEXT : glDeleteQueries;
|
||||||
|
|
||||||
|
if (m_timestamp_query_started)
|
||||||
|
{
|
||||||
|
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
|
||||||
|
EndQuery(GL_TIME_ELAPSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
|
||||||
|
m_timestamp_queries.fill(0);
|
||||||
|
m_read_timestamp_query = 0;
|
||||||
|
m_write_timestamp_query = 0;
|
||||||
|
m_waiting_timestamp_queries = 0;
|
||||||
|
m_timestamp_query_started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::PopTimestampQuery()
|
||||||
|
{
|
||||||
|
const bool gles = m_gl_context->IsGLES();
|
||||||
|
|
||||||
|
if (gles)
|
||||||
|
{
|
||||||
|
GLint disjoint = 0;
|
||||||
|
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
|
||||||
|
if (disjoint)
|
||||||
|
{
|
||||||
|
Log_VerbosePrintf("GPU timing disjoint, resetting.");
|
||||||
|
if (m_timestamp_query_started)
|
||||||
|
glEndQueryEXT(GL_TIME_ELAPSED);
|
||||||
|
|
||||||
|
m_read_timestamp_query = 0;
|
||||||
|
m_write_timestamp_query = 0;
|
||||||
|
m_waiting_timestamp_queries = 0;
|
||||||
|
m_timestamp_query_started = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (m_waiting_timestamp_queries > 0)
|
||||||
|
{
|
||||||
|
const auto GetQueryObjectiv = gles ? glGetQueryObjectivEXT : glGetQueryObjectiv;
|
||||||
|
const auto GetQueryObjectui64v = gles ? glGetQueryObjectui64vEXT : glGetQueryObjectui64v;
|
||||||
|
|
||||||
|
GLint available = 0;
|
||||||
|
GetQueryObjectiv(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT_AVAILABLE, &available);
|
||||||
|
if (!available)
|
||||||
|
break;
|
||||||
|
|
||||||
|
u64 result = 0;
|
||||||
|
GetQueryObjectui64v(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT, &result);
|
||||||
|
m_accumulated_gpu_time += static_cast<float>(static_cast<double>(result) / 1000000.0);
|
||||||
|
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||||
|
m_waiting_timestamp_queries--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_timestamp_query_started)
|
||||||
|
{
|
||||||
|
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
|
||||||
|
EndQuery(GL_TIME_ELAPSED);
|
||||||
|
|
||||||
|
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||||
|
m_timestamp_query_started = false;
|
||||||
|
m_waiting_timestamp_queries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::KickTimestampQuery()
|
||||||
|
{
|
||||||
|
if (m_timestamp_query_started || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool gles = m_gl_context->IsGLES();
|
||||||
|
const auto BeginQuery = gles ? glBeginQueryEXT : glBeginQuery;
|
||||||
|
|
||||||
|
BeginQuery(GL_TIME_ELAPSED, m_timestamp_queries[m_write_timestamp_query]);
|
||||||
|
m_timestamp_query_started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDevice::SetGPUTimingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_gpu_timing_enabled == enabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (enabled && m_gl_context->IsGLES() &&
|
||||||
|
(!GLAD_GL_EXT_disjoint_timer_query || !glGetQueryObjectivEXT || !glGetQueryObjectui64vEXT))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gpu_timing_enabled = enabled;
|
||||||
|
if (m_gpu_timing_enabled)
|
||||||
|
CreateTimestampQueries();
|
||||||
|
else
|
||||||
|
DestroyTimestampQueries();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenGLDevice::GetAndResetAccumulatedGPUTime()
|
||||||
|
{
|
||||||
|
const float value = m_accumulated_gpu_time;
|
||||||
|
m_accumulated_gpu_time = 0.0f;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetActiveTexture(u32 slot)
|
||||||
|
{
|
||||||
|
if (m_last_texture_unit != slot)
|
||||||
|
{
|
||||||
|
m_last_texture_unit = slot;
|
||||||
|
glActiveTexture(GL_TEXTURE0 + slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnbindTexture(GLuint id)
|
||||||
|
{
|
||||||
|
for (u32 slot = 0; slot < MAX_TEXTURE_SAMPLERS; slot++)
|
||||||
|
{
|
||||||
|
auto& ss = m_last_samplers[slot];
|
||||||
|
if (ss.first == id)
|
||||||
|
{
|
||||||
|
ss.first = 0;
|
||||||
|
|
||||||
|
const GLenum unit = GL_TEXTURE0 + slot;
|
||||||
|
if (m_last_texture_unit != unit)
|
||||||
|
{
|
||||||
|
m_last_texture_unit = unit;
|
||||||
|
glActiveTexture(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnbindSampler(GLuint id)
|
||||||
|
{
|
||||||
|
for (u32 slot = 0; slot < MAX_TEXTURE_SAMPLERS; slot++)
|
||||||
|
{
|
||||||
|
auto& ss = m_last_samplers[slot];
|
||||||
|
if (ss.second == id)
|
||||||
|
{
|
||||||
|
ss.second = 0;
|
||||||
|
glBindSampler(slot, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnbindFramebuffer(const OpenGLFramebuffer* fb)
|
||||||
|
{
|
||||||
|
if (m_current_framebuffer == fb)
|
||||||
|
{
|
||||||
|
m_current_framebuffer = nullptr;
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnbindPipeline(const OpenGLPipeline* pl)
|
||||||
|
{
|
||||||
|
if (m_current_pipeline == pl)
|
||||||
|
{
|
||||||
|
m_current_pipeline = nullptr;
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::PreDrawCheck()
|
||||||
|
{
|
||||||
|
DebugAssert(m_current_pipeline);
|
||||||
|
if (m_current_framebuffer)
|
||||||
|
CommitClear(m_current_framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::Draw(u32 vertex_count, u32 base_vertex)
|
||||||
|
{
|
||||||
|
PreDrawCheck();
|
||||||
|
glDrawArrays(m_current_pipeline->GetTopology(), base_vertex, vertex_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
|
||||||
|
{
|
||||||
|
PreDrawCheck();
|
||||||
|
|
||||||
|
const void* indices = reinterpret_cast<const void*>(static_cast<uintptr_t>(base_index) * sizeof(u16));
|
||||||
|
glDrawElementsBaseVertex(m_current_pipeline->GetTopology(), index_count, GL_UNSIGNED_SHORT, indices, base_vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
|
||||||
|
u32* map_base_vertex)
|
||||||
|
{
|
||||||
|
const auto res = m_vertex_buffer->Map(vertex_size, vertex_size * vertex_count);
|
||||||
|
*map_ptr = res.pointer;
|
||||||
|
*map_space = res.space_aligned;
|
||||||
|
*map_base_vertex = res.index_aligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
|
||||||
|
{
|
||||||
|
m_vertex_buffer->Unmap(vertex_size * vertex_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
|
||||||
|
{
|
||||||
|
const auto res = m_index_buffer->Map(sizeof(DrawIndex), sizeof(DrawIndex) * index_count);
|
||||||
|
*map_ptr = static_cast<DrawIndex*>(res.pointer);
|
||||||
|
*map_space = res.space_aligned;
|
||||||
|
*map_base_index = res.index_aligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnmapIndexBuffer(u32 used_index_count)
|
||||||
|
{
|
||||||
|
m_index_buffer->Unmap(sizeof(DrawIndex) * used_index_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::PushUniformBuffer(const void* data, u32 data_size)
|
||||||
|
{
|
||||||
|
const auto res = m_uniform_buffer->Map(m_uniform_buffer_alignment, data_size);
|
||||||
|
std::memcpy(res.pointer, data, data_size);
|
||||||
|
m_uniform_buffer->Unmap(data_size);
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), res.buffer_offset, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLDevice::MapUniformBuffer(u32 size)
|
||||||
|
{
|
||||||
|
const auto res = m_uniform_buffer->Map(m_uniform_buffer_alignment, size);
|
||||||
|
return res.pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnmapUniformBuffer(u32 size)
|
||||||
|
{
|
||||||
|
const u32 pos = m_uniform_buffer->Unmap(size);
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), pos, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetFramebuffer(GPUFramebuffer* fb)
|
||||||
|
{
|
||||||
|
if (m_current_framebuffer == fb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: maybe move clear check here? gets rid of the per-draw overhead
|
||||||
|
m_current_framebuffer = static_cast<OpenGLFramebuffer*>(fb);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_current_framebuffer ? m_current_framebuffer->GetGLId() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler)
|
||||||
|
{
|
||||||
|
DebugAssert(slot < MAX_TEXTURE_SAMPLERS);
|
||||||
|
auto& sslot = m_last_samplers[slot];
|
||||||
|
|
||||||
|
const OpenGLTexture* T = static_cast<const OpenGLTexture*>(texture);
|
||||||
|
const GLuint Tid = T ? T->GetGLId() : 0;
|
||||||
|
if (sslot.first != Tid)
|
||||||
|
{
|
||||||
|
sslot.first = Tid;
|
||||||
|
|
||||||
|
SetActiveTexture(slot);
|
||||||
|
glBindTexture(T ? T->GetGLTarget() : GL_TEXTURE_2D, T ? T->GetGLId() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLuint Sid = sampler ? static_cast<const OpenGLSampler*>(sampler)->GetID() : 0;
|
||||||
|
if (sslot.second != Sid)
|
||||||
|
{
|
||||||
|
sslot.second = Sid;
|
||||||
|
glBindSampler(slot, Sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer)
|
||||||
|
{
|
||||||
|
const OpenGLTextureBuffer* B = static_cast<const OpenGLTextureBuffer*>(buffer);
|
||||||
|
if (!m_features.texture_buffers_emulated_with_ssbo)
|
||||||
|
{
|
||||||
|
const GLuint Tid = B ? B->GetTextureId() : 0;
|
||||||
|
if (m_last_samplers[slot].first != Tid)
|
||||||
|
{
|
||||||
|
m_last_samplers[slot].first = Tid;
|
||||||
|
SetActiveTexture(slot);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, Tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: cache
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, B ? B->GetBuffer()->GetGLBufferId() : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetViewport(s32 x, s32 y, s32 width, s32 height)
|
||||||
|
{
|
||||||
|
// TODO: cache this
|
||||||
|
// TODO: lower-left origin flip for window fb?
|
||||||
|
glViewport(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetScissor(s32 x, s32 y, s32 width, s32 height)
|
||||||
|
{
|
||||||
|
// TODO: cache this
|
||||||
|
// TODO: lower-left origin flip for window fb?
|
||||||
|
glScissor(x, y, width, height);
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gl/context.h"
|
||||||
|
#include "gpu_device.h"
|
||||||
|
#include "gpu_shader_cache.h"
|
||||||
|
#include "opengl_loader.h"
|
||||||
|
#include "opengl_pipeline.h"
|
||||||
|
#include "opengl_texture.h"
|
||||||
|
#include "postprocessing_chain.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/timer.h"
|
||||||
|
#include "common/window_info.h"
|
||||||
|
|
||||||
|
// TODO: build a cache for programs on top of the pipeline cache idea
|
||||||
|
|
||||||
|
class OpenGLFramebuffer;
|
||||||
|
class OpenGLPipeline;
|
||||||
|
class OpenGLStreamBuffer;
|
||||||
|
class OpenGLTexture;
|
||||||
|
|
||||||
|
class OpenGLDevice final : public GPUDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLDevice();
|
||||||
|
~OpenGLDevice();
|
||||||
|
|
||||||
|
ALWAYS_INLINE static OpenGLDevice& GetInstance() { return *static_cast<OpenGLDevice*>(g_host_display.get()); }
|
||||||
|
ALWAYS_INLINE static OpenGLStreamBuffer* GetTextureStreamBuffer()
|
||||||
|
{
|
||||||
|
return GetInstance().m_texture_stream_buffer.get();
|
||||||
|
}
|
||||||
|
static void BindUpdateTextureUnit();
|
||||||
|
|
||||||
|
ALWAYS_INLINE GL::Context* GetGLContext() const { return m_gl_context.get(); }
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
|
||||||
|
bool HasSurface() const override;
|
||||||
|
|
||||||
|
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||||
|
bool SetupDevice() override;
|
||||||
|
|
||||||
|
bool MakeCurrent() override;
|
||||||
|
bool DoneCurrent() override;
|
||||||
|
|
||||||
|
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||||
|
void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||||
|
bool SupportsFullscreen() const override;
|
||||||
|
bool IsFullscreen() override;
|
||||||
|
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||||
|
AdapterAndModeList GetAdapterAndModeList() override;
|
||||||
|
void DestroySurface() override;
|
||||||
|
|
||||||
|
std::string GetShaderCacheBaseName(const std::string_view& type, bool debug) const override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
|
const void* data = nullptr, u32 data_stride = 0,
|
||||||
|
bool dynamic = false) override;
|
||||||
|
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override;
|
||||||
|
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override;
|
||||||
|
|
||||||
|
bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
|
||||||
|
u32 out_data_stride) override;
|
||||||
|
bool SupportsTextureFormat(GPUTexture::Format format) const override;
|
||||||
|
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
|
||||||
|
u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override;
|
||||||
|
void ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
|
||||||
|
u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUFramebuffer> CreateFramebuffer(GPUTexture* rt = nullptr, u32 rt_layer = 0, u32 rt_level = 0,
|
||||||
|
GPUTexture* ds = nullptr, u32 ds_layer = 0,
|
||||||
|
u32 ds_level = 0) override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, gsl::span<const u8> data) override;
|
||||||
|
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source,
|
||||||
|
std::vector<u8>* out_binary = nullptr) override;
|
||||||
|
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config) override;
|
||||||
|
|
||||||
|
void PushDebugGroup(const char* fmt, ...) override;
|
||||||
|
void PopDebugGroup() override;
|
||||||
|
void InsertDebugMessage(const char* fmt, ...) override;
|
||||||
|
|
||||||
|
void MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
|
||||||
|
u32* map_base_vertex) override;
|
||||||
|
void UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) override;
|
||||||
|
void MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) override;
|
||||||
|
void UnmapIndexBuffer(u32 used_index_count) override;
|
||||||
|
void PushUniformBuffer(const void* data, u32 data_size) override;
|
||||||
|
void* MapUniformBuffer(u32 size) override;
|
||||||
|
void UnmapUniformBuffer(u32 size);
|
||||||
|
void SetFramebuffer(GPUFramebuffer* fb) override;
|
||||||
|
void SetPipeline(GPUPipeline* pipeline) override;
|
||||||
|
void SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) override;
|
||||||
|
void SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer) override;
|
||||||
|
void SetViewport(s32 x, s32 y, s32 width, s32 height) override;
|
||||||
|
void SetScissor(s32 x, s32 y, s32 width, s32 height) override;
|
||||||
|
void Draw(u32 vertex_count, u32 base_vertex) override;
|
||||||
|
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||||
|
|
||||||
|
void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
|
bool BeginPresent(bool skip_present) override;
|
||||||
|
void EndPresent() override;
|
||||||
|
|
||||||
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
|
void CommitClear(OpenGLTexture* tex);
|
||||||
|
void CommitClear(OpenGLFramebuffer* fb); // Assumes the FB has been bound.
|
||||||
|
|
||||||
|
GLuint LookupProgramCache(const OpenGLPipeline::ProgramCacheKey& key, const GPUPipeline::GraphicsConfig& plconfig);
|
||||||
|
GLuint CompileProgram(const GPUPipeline::GraphicsConfig& plconfig);
|
||||||
|
void UnrefProgram(const OpenGLPipeline::ProgramCacheKey& key);
|
||||||
|
|
||||||
|
GLuint LookupVAOCache(const OpenGLPipeline::VertexArrayCacheKey& key);
|
||||||
|
GLuint CreateVAO(gsl::span<const GPUPipeline::VertexAttribute> attributes, u32 stride);
|
||||||
|
void UnrefVAO(const OpenGLPipeline::VertexArrayCacheKey& key);
|
||||||
|
|
||||||
|
void SetActiveTexture(u32 slot);
|
||||||
|
void UnbindTexture(GLuint id);
|
||||||
|
void UnbindSampler(GLuint id);
|
||||||
|
void UnbindFramebuffer(const OpenGLFramebuffer* fb);
|
||||||
|
void UnbindPipeline(const OpenGLPipeline* pl);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static constexpr u8 NUM_TIMESTAMP_QUERIES = 3;
|
||||||
|
|
||||||
|
static constexpr GLenum UPDATE_TEXTURE_UNIT = GL_TEXTURE8;
|
||||||
|
|
||||||
|
static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||||
|
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||||
|
static constexpr u32 UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||||
|
static constexpr u32 TEXTURE_STREAM_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||||
|
|
||||||
|
// TODO: pass in file instead of blob for pipeline cache
|
||||||
|
OpenGLPipeline::VertexArrayCache m_vao_cache;
|
||||||
|
OpenGLPipeline::ProgramCache m_program_cache;
|
||||||
|
|
||||||
|
bool CreateBuffers();
|
||||||
|
void DestroyBuffers();
|
||||||
|
|
||||||
|
void SetSwapInterval();
|
||||||
|
|
||||||
|
void CreateTimestampQueries();
|
||||||
|
void DestroyTimestampQueries();
|
||||||
|
void PopTimestampQuery();
|
||||||
|
void KickTimestampQuery();
|
||||||
|
|
||||||
|
bool CheckFeatures();
|
||||||
|
|
||||||
|
void PreDrawCheck();
|
||||||
|
|
||||||
|
std::unique_ptr<GL::Context> m_gl_context;
|
||||||
|
std::unique_ptr<OpenGLFramebuffer> m_window_framebuffer;
|
||||||
|
|
||||||
|
GLuint m_uniform_buffer_alignment = 1;
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> m_vertex_buffer;
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> m_index_buffer;
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> m_uniform_buffer;
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> m_texture_stream_buffer;
|
||||||
|
|
||||||
|
// VAO cache - fixed max as key
|
||||||
|
GPUPipeline::RasterizationState m_last_rasterization_state = {};
|
||||||
|
GPUPipeline::DepthState m_last_depth_state = {};
|
||||||
|
GPUPipeline::BlendState m_last_blend_state = {};
|
||||||
|
GLuint m_last_program = 0;
|
||||||
|
GLuint m_last_vao = 0;
|
||||||
|
u32 m_last_texture_unit = 0;
|
||||||
|
std::array<std::pair<GLuint, GLuint>, MAX_TEXTURE_SAMPLERS> m_last_samplers = {};
|
||||||
|
|
||||||
|
// Misc framebuffers
|
||||||
|
GLuint m_read_fbo = 0;
|
||||||
|
GLuint m_write_fbo = 0;
|
||||||
|
|
||||||
|
OpenGLFramebuffer* m_current_framebuffer = nullptr;
|
||||||
|
OpenGLPipeline* m_current_pipeline = nullptr;
|
||||||
|
|
||||||
|
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||||
|
float m_accumulated_gpu_time = 0.0f;
|
||||||
|
u8 m_read_timestamp_query = 0;
|
||||||
|
u8 m_write_timestamp_query = 0;
|
||||||
|
u8 m_waiting_timestamp_queries = 0;
|
||||||
|
bool m_timestamp_query_started = false;
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,110 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "common/timer.h"
|
|
||||||
#include "common/window_info.h"
|
|
||||||
#include "gl/context.h"
|
|
||||||
#include "gl/loader.h"
|
|
||||||
#include "gl/program.h"
|
|
||||||
#include "gl/stream_buffer.h"
|
|
||||||
#include "gl/texture.h"
|
|
||||||
#include "gpu_device.h"
|
|
||||||
#include "postprocessing_chain.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class OpenGLGPUDevice final : public GPUDevice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OpenGLGPUDevice();
|
|
||||||
~OpenGLGPUDevice();
|
|
||||||
|
|
||||||
RenderAPI GetRenderAPI() const override;
|
|
||||||
|
|
||||||
bool HasSurface() const override;
|
|
||||||
|
|
||||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
|
||||||
bool SetupDevice() override;
|
|
||||||
|
|
||||||
bool MakeCurrent() override;
|
|
||||||
bool DoneCurrent() override;
|
|
||||||
|
|
||||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
|
||||||
void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
|
||||||
bool SupportsFullscreen() const override;
|
|
||||||
bool IsFullscreen() override;
|
|
||||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
|
||||||
AdapterAndModeList GetAdapterAndModeList() override;
|
|
||||||
void DestroySurface() override;
|
|
||||||
|
|
||||||
bool SetPostProcessingChain(const std::string_view& config) override;
|
|
||||||
|
|
||||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
|
||||||
GPUTexture::Type type, GPUTexture::Format format, const void* data,
|
|
||||||
u32 data_stride, bool dynamic = false) override;
|
|
||||||
bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
|
|
||||||
u32 out_data_stride) override;
|
|
||||||
bool SupportsTextureFormat(GPUTexture::Format format) const override;
|
|
||||||
|
|
||||||
void SetVSync(bool enabled) override;
|
|
||||||
|
|
||||||
bool Render(bool skip_present) override;
|
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
|
||||||
|
|
||||||
ALWAYS_INLINE GL::Context* GetGLContext() const { return m_gl_context.get(); }
|
|
||||||
ALWAYS_INLINE bool UsePBOForUploads() const { return m_use_pbo_for_pixels; }
|
|
||||||
ALWAYS_INLINE bool UseGLES3DrawPath() const { return m_use_gles2_draw_path; }
|
|
||||||
ALWAYS_INLINE std::vector<u8>& GetTextureRepackBuffer() { return m_texture_repack_buffer; }
|
|
||||||
|
|
||||||
GL::StreamBuffer* GetTextureStreamBuffer();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static constexpr u8 NUM_TIMESTAMP_QUERIES = 3;
|
|
||||||
|
|
||||||
const char* GetGLSLVersionString() const;
|
|
||||||
std::string GetGLSLVersionHeader() const;
|
|
||||||
|
|
||||||
bool CreateResources() override;
|
|
||||||
void DestroyResources() override;
|
|
||||||
|
|
||||||
void SetSwapInterval();
|
|
||||||
|
|
||||||
void RenderDisplay();
|
|
||||||
void RenderImGui();
|
|
||||||
void RenderSoftwareCursor();
|
|
||||||
|
|
||||||
void RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, GL::Texture* texture, s32 texture_view_x,
|
|
||||||
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
|
|
||||||
void RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32 height, GPUTexture* texture_handle);
|
|
||||||
|
|
||||||
void CreateTimestampQueries();
|
|
||||||
void DestroyTimestampQueries();
|
|
||||||
void PopTimestampQuery();
|
|
||||||
void KickTimestampQuery();
|
|
||||||
|
|
||||||
std::unique_ptr<GL::Context> m_gl_context;
|
|
||||||
|
|
||||||
GL::Program m_display_program;
|
|
||||||
GL::Program m_cursor_program;
|
|
||||||
GLuint m_display_vao = 0;
|
|
||||||
GLuint m_display_nearest_sampler = 0;
|
|
||||||
GLuint m_display_linear_sampler = 0;
|
|
||||||
GLuint m_display_border_sampler = 0;
|
|
||||||
GLuint m_uniform_buffer_alignment = 1;
|
|
||||||
|
|
||||||
std::unique_ptr<GL::StreamBuffer> m_texture_stream_buffer;
|
|
||||||
std::vector<u8> m_texture_repack_buffer;
|
|
||||||
u32 m_texture_stream_buffer_offset = 0;
|
|
||||||
|
|
||||||
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
|
||||||
float m_accumulated_gpu_time = 0.0f;
|
|
||||||
u8 m_read_timestamp_query = 0;
|
|
||||||
u8 m_write_timestamp_query = 0;
|
|
||||||
u8 m_waiting_timestamp_queries = 0;
|
|
||||||
bool m_timestamp_query_started = false;
|
|
||||||
|
|
||||||
bool m_use_gles2_draw_path = false;
|
|
||||||
bool m_use_pbo_for_pixels = false;
|
|
||||||
};
|
|
|
@ -0,0 +1,545 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#include "opengl_pipeline.h"
|
||||||
|
#include "../settings.h"
|
||||||
|
#include "../shadergen.h"
|
||||||
|
#include "opengl_device.h"
|
||||||
|
#include "opengl_stream_buffer.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/file_system.h"
|
||||||
|
#include "common/hash_combine.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/path.h"
|
||||||
|
|
||||||
|
Log_SetChannel(OpenGLPipeline);
|
||||||
|
|
||||||
|
static unsigned s_next_bad_shader_id = 1;
|
||||||
|
|
||||||
|
static GLenum GetGLShaderType(GPUShaderStage stage)
|
||||||
|
{
|
||||||
|
static constexpr std::array<GLenum, static_cast<u32>(GPUShaderStage::MaxCount)> mapping = {{
|
||||||
|
GL_VERTEX_SHADER, // Vertex
|
||||||
|
GL_FRAGMENT_SHADER, // Fragment
|
||||||
|
GL_COMPUTE_SHADER, // Compute
|
||||||
|
}};
|
||||||
|
|
||||||
|
return mapping[static_cast<u32>(stage)];
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLShader::OpenGLShader(GPUShaderStage stage, GLuint id, const GPUShaderCache::CacheIndexKey& key)
|
||||||
|
: GPUShader(stage), m_id(id), m_key(key)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLShader::~OpenGLShader() = default;
|
||||||
|
|
||||||
|
void OpenGLShader::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (glObjectLabel)
|
||||||
|
glObjectLabel(GL_SHADER, m_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUShader> OpenGLDevice::CreateShaderFromBinary(GPUShaderStage stage, gsl::span<const u8> data)
|
||||||
|
{
|
||||||
|
// Not supported.. except spir-v maybe? but no point really...
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUShader> OpenGLDevice::CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source,
|
||||||
|
std::vector<u8>* out_binary)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
|
||||||
|
GLuint shader = glCreateShader(GetGLShaderType(stage));
|
||||||
|
if (GLenum err = glGetError(); err != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("glCreateShader() failed: %u", err);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLchar* string = source.data();
|
||||||
|
const GLint length = static_cast<GLint>(source.length());
|
||||||
|
glShaderSource(shader, 1, &string, &length);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
GLint status = GL_FALSE;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||||
|
|
||||||
|
GLint info_log_length = 0;
|
||||||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||||
|
|
||||||
|
if (status == GL_FALSE || info_log_length > 0)
|
||||||
|
{
|
||||||
|
std::string info_log;
|
||||||
|
info_log.resize(info_log_length + 1);
|
||||||
|
glGetShaderInfoLog(shader, info_log_length, &info_log_length, &info_log[0]);
|
||||||
|
|
||||||
|
if (status == GL_TRUE)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Shader compiled with warnings:\n%s", info_log.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Shader failed to compile:\n%s", info_log.c_str());
|
||||||
|
|
||||||
|
auto fp = FileSystem::OpenManagedCFile(
|
||||||
|
Path::Combine(EmuFolders::DataRoot, fmt::format("bad_shader_{}.txt", s_next_bad_shader_id++)).c_str(), "wb");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
std::fwrite(source.data(), source.size(), 1, fp.get());
|
||||||
|
std::fprintf(fp.get(), "\n\nCompile %s shader failed\n", GPUShader::GetStageName(stage));
|
||||||
|
std::fwrite(info_log.c_str(), info_log_length, 1, fp.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteShader(shader);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unique_ptr<GPUShader>(
|
||||||
|
new OpenGLShader(stage, shader, GPUShaderCache::GetCacheKey(stage, source, "main")));
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool OpenGLPipeline::VertexArrayCacheKey::operator==(const VertexArrayCacheKey& rhs) const
|
||||||
|
{
|
||||||
|
return (std::memcmp(this, &rhs, sizeof(*this)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLPipeline::VertexArrayCacheKey::operator!=(const VertexArrayCacheKey& rhs) const
|
||||||
|
{
|
||||||
|
return (std::memcmp(this, &rhs, sizeof(*this)) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t OpenGLPipeline::VertexArrayCacheKeyHash::operator()(const VertexArrayCacheKey& k) const
|
||||||
|
{
|
||||||
|
std::size_t h = 0;
|
||||||
|
hash_combine(h, k.num_vertex_attributes, k.vertex_attribute_stride);
|
||||||
|
for (const VertexAttribute& va : k.vertex_attributes)
|
||||||
|
hash_combine(h, va.key);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLPipeline::ProgramCacheKey::operator==(const ProgramCacheKey& rhs) const
|
||||||
|
{
|
||||||
|
return (std::memcmp(this, &rhs, sizeof(*this)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLPipeline::ProgramCacheKey::operator!=(const ProgramCacheKey& rhs) const
|
||||||
|
{
|
||||||
|
return (std::memcmp(this, &rhs, sizeof(*this)) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t OpenGLPipeline::ProgramCacheKeyHash::operator()(const ProgramCacheKey& k) const
|
||||||
|
{
|
||||||
|
// TODO: maybe use xxhash here...
|
||||||
|
std::size_t h = 0;
|
||||||
|
hash_combine(h, k.vs_key.entry_point_low, k.vs_key.entry_point_high, k.vs_key.source_hash_low,
|
||||||
|
k.vs_key.source_hash_high, k.vs_key.source_length, k.vs_key.shader_type);
|
||||||
|
hash_combine(h, k.fs_key.entry_point_low, k.fs_key.entry_point_high, k.fs_key.source_hash_low,
|
||||||
|
k.fs_key.source_hash_high, k.fs_key.source_length, k.fs_key.shader_type);
|
||||||
|
hash_combine(h, k.va_key.num_vertex_attributes, k.va_key.vertex_attribute_stride);
|
||||||
|
for (const VertexAttribute& va : k.va_key.vertex_attributes)
|
||||||
|
hash_combine(h, va.key);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLPipeline::ProgramCacheKey OpenGLPipeline::GetProgramCacheKey(const GraphicsConfig& plconfig)
|
||||||
|
{
|
||||||
|
Assert(plconfig.input_layout.vertex_attributes.size() <= MAX_VERTEX_ATTRIBUTES);
|
||||||
|
|
||||||
|
ProgramCacheKey ret;
|
||||||
|
ret.vs_key = static_cast<const OpenGLShader*>(plconfig.vertex_shader)->GetKey();
|
||||||
|
ret.fs_key = static_cast<const OpenGLShader*>(plconfig.fragment_shader)->GetKey();
|
||||||
|
|
||||||
|
ret.va_key.num_vertex_attributes = static_cast<u32>(plconfig.input_layout.vertex_attributes.size());
|
||||||
|
ret.va_key.vertex_attribute_stride = plconfig.input_layout.vertex_stride;
|
||||||
|
std::memset(ret.va_key.vertex_attributes, 0, sizeof(ret.va_key.vertex_attributes));
|
||||||
|
if (ret.va_key.num_vertex_attributes > 0)
|
||||||
|
{
|
||||||
|
std::memcpy(ret.va_key.vertex_attributes, plconfig.input_layout.vertex_attributes.data(),
|
||||||
|
sizeof(VertexAttribute) * ret.va_key.num_vertex_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint OpenGLDevice::LookupProgramCache(const OpenGLPipeline::ProgramCacheKey& key,
|
||||||
|
const GPUPipeline::GraphicsConfig& plconfig)
|
||||||
|
{
|
||||||
|
auto it = m_program_cache.find(key);
|
||||||
|
if (it != m_program_cache.end())
|
||||||
|
{
|
||||||
|
if (it->second.program_id == 0)
|
||||||
|
{
|
||||||
|
// TODO: read it from file instead
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it->second.reference_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second.program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLPipeline::ProgramCacheItem item;
|
||||||
|
item.program_id = CompileProgram(plconfig);
|
||||||
|
item.reference_count = 0;
|
||||||
|
item.file_offset = std::numeric_limits<decltype(item.file_offset)>::max();
|
||||||
|
if (item.program_id != 0)
|
||||||
|
{
|
||||||
|
item.reference_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert into cache even if we failed, so we don't compile it again, but don't increment reference count.
|
||||||
|
m_program_cache.emplace(key, item);
|
||||||
|
return item.program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint OpenGLDevice::CompileProgram(const GPUPipeline::GraphicsConfig& plconfig)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
const GLuint program_id = glCreateProgram();
|
||||||
|
if (glGetError() != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create program object.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(plconfig.vertex_shader && plconfig.fragment_shader);
|
||||||
|
glAttachShader(program_id, static_cast<const OpenGLShader*>(plconfig.vertex_shader)->GetGLId());
|
||||||
|
glAttachShader(program_id, static_cast<const OpenGLShader*>(plconfig.fragment_shader)->GetGLId());
|
||||||
|
|
||||||
|
if (!ShaderGen::UseGLSLBindingLayout())
|
||||||
|
{
|
||||||
|
// TODO: fixme, need semantic type...
|
||||||
|
#if 0
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(plconfig.input_layout.vertex_attributes.size()); i++)
|
||||||
|
glBindAttribLocation(program_id, i, fmt::format(""));
|
||||||
|
|
||||||
|
glBindFragDataLocation(program_id, 0, "o_col0");
|
||||||
|
|
||||||
|
if (m_features.dual_source_blend)
|
||||||
|
{
|
||||||
|
if (GLAD_GL_VERSION_3_3 || GLAD_GL_ARB_blend_func_extended)
|
||||||
|
{
|
||||||
|
glBindFragDataLocationIndexed(program_id, 1, 0, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (GLAD_GL_EXT_blend_func_extended)
|
||||||
|
{
|
||||||
|
glBindFragDataLocationIndexedEXT(program_id, 1, 0, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_ErrorPrintf("BindFragDataIndexed() called without ARB or EXT extension, we'll probably crash.");
|
||||||
|
glBindFragDataLocationIndexed(program_id, 1, 0, name);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Panic("Fixme");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
glLinkProgram(program_id);
|
||||||
|
|
||||||
|
GLint status = GL_FALSE;
|
||||||
|
glGetProgramiv(program_id, GL_LINK_STATUS, &status);
|
||||||
|
|
||||||
|
GLint info_log_length = 0;
|
||||||
|
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||||
|
|
||||||
|
if (status == GL_FALSE || info_log_length > 0)
|
||||||
|
{
|
||||||
|
std::string info_log;
|
||||||
|
info_log.resize(info_log_length + 1);
|
||||||
|
glGetProgramInfoLog(program_id, info_log_length, &info_log_length, &info_log[0]);
|
||||||
|
|
||||||
|
if (status == GL_TRUE)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Program linked with warnings:\n%s", info_log.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Program failed to link:\n%s", info_log.c_str());
|
||||||
|
glDeleteProgram(program_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnrefProgram(const OpenGLPipeline::ProgramCacheKey& key)
|
||||||
|
{
|
||||||
|
auto it = m_program_cache.find(key);
|
||||||
|
Assert(it != m_program_cache.end() && it->second.program_id != 0 && it->second.reference_count > 0);
|
||||||
|
|
||||||
|
if ((--it->second.reference_count) > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_last_program == it->second.program_id)
|
||||||
|
{
|
||||||
|
m_last_program = 0;
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteProgram(it->second.program_id);
|
||||||
|
it->second.program_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint OpenGLDevice::LookupVAOCache(const OpenGLPipeline::VertexArrayCacheKey& key)
|
||||||
|
{
|
||||||
|
auto it = m_vao_cache.find(key);
|
||||||
|
if (it != m_vao_cache.end())
|
||||||
|
{
|
||||||
|
it->second.reference_count++;
|
||||||
|
return it->second.vao_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLPipeline::VertexArrayCacheItem item;
|
||||||
|
item.vao_id =
|
||||||
|
CreateVAO(gsl::span<const GPUPipeline::VertexAttribute>(key.vertex_attributes, key.num_vertex_attributes),
|
||||||
|
key.vertex_attribute_stride);
|
||||||
|
if (item.vao_id == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
item.reference_count = 1;
|
||||||
|
m_vao_cache.emplace(key, item);
|
||||||
|
return item.vao_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint OpenGLDevice::CreateVAO(gsl::span<const GPUPipeline::VertexAttribute> attributes, u32 stride)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
GLuint vao;
|
||||||
|
glGenVertexArrays(1, &vao);
|
||||||
|
if (const GLenum err = glGetError(); err != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create vertex array object: %u", vao);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
m_vertex_buffer->Bind();
|
||||||
|
m_index_buffer->Bind();
|
||||||
|
|
||||||
|
struct VAMapping
|
||||||
|
{
|
||||||
|
GLenum type;
|
||||||
|
GLboolean normalized;
|
||||||
|
GLboolean integer;
|
||||||
|
};
|
||||||
|
static constexpr u32 MAX_COMPONENTS = 4;
|
||||||
|
static constexpr const std::array<VAMapping, static_cast<u8>(GPUPipeline::VertexAttribute::Type::MaxCount)>
|
||||||
|
format_mapping = {{
|
||||||
|
{GL_FLOAT, GL_FALSE, GL_FALSE}, // Float
|
||||||
|
{GL_UNSIGNED_BYTE, GL_FALSE, GL_TRUE}, // UInt8
|
||||||
|
{GL_BYTE, GL_FALSE, GL_TRUE}, // SInt8
|
||||||
|
{GL_UNSIGNED_BYTE, GL_TRUE, GL_FALSE}, // UNorm8
|
||||||
|
{GL_UNSIGNED_SHORT, GL_FALSE, GL_TRUE}, // UInt16
|
||||||
|
{GL_SHORT, GL_FALSE, GL_TRUE}, // SInt16
|
||||||
|
{GL_UNSIGNED_SHORT, GL_TRUE, GL_FALSE}, // UNorm16
|
||||||
|
{GL_UNSIGNED_INT, GL_FALSE, GL_TRUE}, // UInt32
|
||||||
|
{GL_INT, GL_FALSE, GL_TRUE}, // SInt32
|
||||||
|
}};
|
||||||
|
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(attributes.size()); i++)
|
||||||
|
{
|
||||||
|
const GPUPipeline::VertexAttribute& va = attributes[i];
|
||||||
|
const VAMapping& m = format_mapping[static_cast<u8>(va.type.GetValue())];
|
||||||
|
const void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(va.offset.GetValue()));
|
||||||
|
glEnableVertexAttribArray(i);
|
||||||
|
if (m.integer)
|
||||||
|
glVertexAttribIPointer(i, va.components, m.type, stride, ptr);
|
||||||
|
else
|
||||||
|
glVertexAttribPointer(i, va.components, m.type, m.normalized, stride, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(m_last_vao);
|
||||||
|
|
||||||
|
return vao;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::UnrefVAO(const OpenGLPipeline::VertexArrayCacheKey& key)
|
||||||
|
{
|
||||||
|
auto it = m_vao_cache.find(key);
|
||||||
|
Assert(it != m_vao_cache.end() && it->second.reference_count > 0);
|
||||||
|
|
||||||
|
if ((--it->second.reference_count) > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_last_vao == it->second.vao_id)
|
||||||
|
{
|
||||||
|
m_last_vao = 0;
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteVertexArrays(1, &it->second.vao_id);
|
||||||
|
m_vao_cache.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLPipeline::OpenGLPipeline(const ProgramCacheKey& key, GLuint program, GLuint vao, const RasterizationState& rs,
|
||||||
|
const DepthState& ds, const BlendState& bs, GLenum topology)
|
||||||
|
: m_key(key), m_program(program), m_vao(vao), m_rasterization_state(rs), m_depth_state(ds), m_blend_state(bs),
|
||||||
|
m_topology(topology)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLPipeline::~OpenGLPipeline()
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetInstance().UnbindPipeline(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLPipeline::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (glObjectLabel)
|
||||||
|
glObjectLabel(GL_PROGRAM, m_program, static_cast<u32>(name.length()), name.data());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUPipeline> OpenGLDevice::CreatePipeline(const GPUPipeline::GraphicsConfig& config)
|
||||||
|
{
|
||||||
|
const OpenGLPipeline::ProgramCacheKey pkey = OpenGLPipeline::GetProgramCacheKey(config);
|
||||||
|
|
||||||
|
const GLuint program_id = LookupProgramCache(pkey, config);
|
||||||
|
if (program_id == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const GLuint vao_id = LookupVAOCache(pkey.va_key);
|
||||||
|
if (vao_id == 0)
|
||||||
|
{
|
||||||
|
UnrefProgram(pkey);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<GLenum, static_cast<u32>(GPUPipeline::Primitive::MaxCount)> primitives = {{
|
||||||
|
GL_POINTS, // Points
|
||||||
|
GL_LINES, // Lines
|
||||||
|
GL_TRIANGLES, // Triangles
|
||||||
|
GL_TRIANGLE_STRIP, // TriangleStrips
|
||||||
|
}};
|
||||||
|
|
||||||
|
return std::unique_ptr<GPUPipeline>(new OpenGLPipeline(pkey, program_id, vao_id, config.rasterization, config.depth,
|
||||||
|
config.blend, primitives[static_cast<u8>(config.primitive)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static void ApplyRasterizationState(const GPUPipeline::RasterizationState& rs)
|
||||||
|
{
|
||||||
|
if (rs.cull_mode == GPUPipeline::CullMode::None)
|
||||||
|
{
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glCullFace((rs.cull_mode == GPUPipeline::CullMode::Front) ? GL_FRONT : GL_BACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: always enabled, should be done at init time
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static void ApplyDepthState(const GPUPipeline::DepthState& ds)
|
||||||
|
{
|
||||||
|
static constexpr std::array<GLenum, static_cast<u32>(GPUPipeline::DepthFunc::MaxCount)> func_mapping = {{
|
||||||
|
GL_NEVER, // Never
|
||||||
|
GL_ALWAYS, // Always
|
||||||
|
GL_LESS, // Less
|
||||||
|
GL_LEQUAL, // LessEqual
|
||||||
|
GL_GREATER, // Greater
|
||||||
|
GL_GEQUAL, // GreaterEqual
|
||||||
|
GL_EQUAL, // Equal
|
||||||
|
}};
|
||||||
|
|
||||||
|
(ds.depth_test != GPUPipeline::DepthFunc::Never) ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(func_mapping[static_cast<u8>(ds.depth_test.GetValue())]);
|
||||||
|
glDepthMask(ds.depth_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static void ApplyBlendState(const GPUPipeline::BlendState& bs)
|
||||||
|
{
|
||||||
|
static constexpr std::array<GLenum, static_cast<u32>(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{
|
||||||
|
GL_ZERO, // Zero
|
||||||
|
GL_ONE, // One
|
||||||
|
GL_SRC_COLOR, // SrcColor
|
||||||
|
GL_ONE_MINUS_SRC_COLOR, // InvSrcColor
|
||||||
|
GL_DST_COLOR, // DstColor
|
||||||
|
GL_ONE_MINUS_DST_COLOR, // InvDstColor
|
||||||
|
GL_SRC_ALPHA, // SrcAlpha
|
||||||
|
GL_ONE_MINUS_SRC_ALPHA, // InvSrcAlpha
|
||||||
|
GL_SRC1_ALPHA, // SrcAlpha1
|
||||||
|
GL_ONE_MINUS_SRC1_ALPHA, // InvSrcAlpha1
|
||||||
|
GL_DST_ALPHA, // DstAlpha
|
||||||
|
GL_ONE_MINUS_DST_ALPHA, // InvDstAlpha
|
||||||
|
GL_CONSTANT_COLOR, // ConstantColor
|
||||||
|
GL_ONE_MINUS_CONSTANT_COLOR, // InvConstantColor
|
||||||
|
}};
|
||||||
|
|
||||||
|
static constexpr std::array<GLenum, static_cast<u32>(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{
|
||||||
|
GL_FUNC_ADD, // Add
|
||||||
|
GL_FUNC_SUBTRACT, // Subtract
|
||||||
|
GL_FUNC_REVERSE_SUBTRACT, // ReverseSubtract
|
||||||
|
GL_MIN, // Min
|
||||||
|
GL_MAX, // Max
|
||||||
|
}};
|
||||||
|
|
||||||
|
// TODO: driver bugs
|
||||||
|
|
||||||
|
bs.enable ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
if (bs.enable)
|
||||||
|
{
|
||||||
|
glBlendFuncSeparate(blend_mapping[static_cast<u8>(bs.src_blend.GetValue())],
|
||||||
|
blend_mapping[static_cast<u8>(bs.dst_blend.GetValue())],
|
||||||
|
blend_mapping[static_cast<u8>(bs.src_alpha_blend.GetValue())],
|
||||||
|
blend_mapping[static_cast<u8>(bs.dst_alpha_blend.GetValue())]);
|
||||||
|
glBlendEquationSeparate(op_mapping[static_cast<u8>(bs.blend_op.GetValue())],
|
||||||
|
op_mapping[static_cast<u8>(bs.alpha_blend_op.GetValue())]);
|
||||||
|
|
||||||
|
// TODO: cache this to avoid calls?
|
||||||
|
glBlendColor(bs.GetConstantRed(), bs.GetConstantGreen(), bs.GetConstantBlue(), bs.GetConstantAlpha());
|
||||||
|
}
|
||||||
|
|
||||||
|
glColorMask(bs.write_r, bs.write_g, bs.write_b, bs.write_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::SetPipeline(GPUPipeline* pipeline)
|
||||||
|
{
|
||||||
|
if (m_current_pipeline == pipeline)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OpenGLPipeline* const P = static_cast<OpenGLPipeline*>(pipeline);
|
||||||
|
m_current_pipeline = P;
|
||||||
|
|
||||||
|
if (m_last_rasterization_state != P->GetRasterizationState())
|
||||||
|
{
|
||||||
|
m_last_rasterization_state = P->GetRasterizationState();
|
||||||
|
ApplyRasterizationState(m_last_rasterization_state);
|
||||||
|
}
|
||||||
|
if (m_last_depth_state != P->GetDepthState())
|
||||||
|
{
|
||||||
|
m_last_depth_state = P->GetDepthState();
|
||||||
|
ApplyDepthState(m_last_depth_state);
|
||||||
|
}
|
||||||
|
if (m_last_blend_state != P->GetBlendState())
|
||||||
|
{
|
||||||
|
m_last_blend_state = P->GetBlendState();
|
||||||
|
ApplyBlendState(m_last_blend_state);
|
||||||
|
}
|
||||||
|
if (m_last_vao != P->GetVAO())
|
||||||
|
{
|
||||||
|
m_last_vao = P->GetVAO();
|
||||||
|
glBindVertexArray(m_last_vao);
|
||||||
|
}
|
||||||
|
if (m_last_program != P->GetProgram())
|
||||||
|
{
|
||||||
|
m_last_program = P->GetProgram();
|
||||||
|
glUseProgram(m_last_program);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gpu_device.h"
|
||||||
|
#include "gpu_shader_cache.h"
|
||||||
|
#include "opengl_loader.h"
|
||||||
|
|
||||||
|
class OpenGLDevice;
|
||||||
|
|
||||||
|
class OpenGLShader final : public GPUShader
|
||||||
|
{
|
||||||
|
friend OpenGLDevice;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~OpenGLShader() override;
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE GLuint GetGLId() const { return m_id; }
|
||||||
|
ALWAYS_INLINE const GPUShaderCache::CacheIndexKey& GetKey() const { return m_key; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenGLShader(GPUShaderStage stage, GLuint id, const GPUShaderCache::CacheIndexKey& key);
|
||||||
|
|
||||||
|
GLuint m_id;
|
||||||
|
GPUShaderCache::CacheIndexKey m_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenGLPipeline final : public GPUPipeline
|
||||||
|
{
|
||||||
|
friend OpenGLDevice;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr u32 MAX_VERTEX_ATTRIBUTES = 5;
|
||||||
|
|
||||||
|
struct VertexArrayCacheKey
|
||||||
|
{
|
||||||
|
VertexAttribute vertex_attributes[MAX_VERTEX_ATTRIBUTES];
|
||||||
|
u32 vertex_attribute_stride;
|
||||||
|
u32 num_vertex_attributes;
|
||||||
|
|
||||||
|
bool operator==(const VertexArrayCacheKey& rhs) const;
|
||||||
|
bool operator!=(const VertexArrayCacheKey& rhs) const;
|
||||||
|
};
|
||||||
|
struct VertexArrayCacheItem
|
||||||
|
{
|
||||||
|
GLuint vao_id;
|
||||||
|
u32 reference_count;
|
||||||
|
};
|
||||||
|
struct VertexArrayCacheKeyHash
|
||||||
|
{
|
||||||
|
size_t operator()(const VertexArrayCacheKey& k) const;
|
||||||
|
};
|
||||||
|
using VertexArrayCache = std::unordered_map<VertexArrayCacheKey, VertexArrayCacheItem, VertexArrayCacheKeyHash>;
|
||||||
|
|
||||||
|
struct ProgramCacheKey
|
||||||
|
{
|
||||||
|
GPUShaderCache::CacheIndexKey vs_key;
|
||||||
|
GPUShaderCache::CacheIndexKey fs_key;
|
||||||
|
VertexArrayCacheKey va_key;
|
||||||
|
|
||||||
|
bool operator==(const ProgramCacheKey& rhs) const;
|
||||||
|
bool operator!=(const ProgramCacheKey& rhs) const;
|
||||||
|
};
|
||||||
|
struct ProgramCacheKeyHash
|
||||||
|
{
|
||||||
|
size_t operator()(const ProgramCacheKey& k) const;
|
||||||
|
};
|
||||||
|
struct ProgramCacheItem
|
||||||
|
{
|
||||||
|
GLuint program_id;
|
||||||
|
GLuint vao_id;
|
||||||
|
u32 reference_count;
|
||||||
|
u32 file_offset;
|
||||||
|
};
|
||||||
|
using ProgramCache = std::unordered_map<ProgramCacheKey, ProgramCacheItem, ProgramCacheKeyHash>;
|
||||||
|
|
||||||
|
static ProgramCacheKey GetProgramCacheKey(const GraphicsConfig& plconfig);
|
||||||
|
|
||||||
|
~OpenGLPipeline() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE GLuint GetProgram() const { return m_program; }
|
||||||
|
ALWAYS_INLINE GLuint GetVAO() const { return m_vao; }
|
||||||
|
ALWAYS_INLINE const RasterizationState& GetRasterizationState() const { return m_rasterization_state; }
|
||||||
|
ALWAYS_INLINE const DepthState& GetDepthState() const { return m_depth_state; }
|
||||||
|
ALWAYS_INLINE const BlendState& GetBlendState() const { return m_blend_state; }
|
||||||
|
ALWAYS_INLINE GLenum GetTopology() const { return m_topology; }
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenGLPipeline(const ProgramCacheKey& key, GLuint program, GLuint vao, const RasterizationState& rs,
|
||||||
|
const DepthState& ds, const BlendState& bs, GLenum topology);
|
||||||
|
|
||||||
|
ProgramCacheKey m_key;
|
||||||
|
GLuint m_program;
|
||||||
|
GLuint m_vao;
|
||||||
|
RasterizationState m_rasterization_state;
|
||||||
|
DepthState m_depth_state;
|
||||||
|
BlendState m_blend_state;
|
||||||
|
GLenum m_topology;
|
||||||
|
};
|
|
@ -1,57 +1,71 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "stream_buffer.h"
|
#include "opengl_stream_buffer.h"
|
||||||
|
|
||||||
#include "common/align.h"
|
#include "common/align.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
namespace GL {
|
OpenGLStreamBuffer::OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||||
|
|
||||||
StreamBuffer::StreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
|
||||||
: m_target(target), m_buffer_id(buffer_id), m_size(size)
|
: m_target(target), m_buffer_id(buffer_id), m_size(size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamBuffer::~StreamBuffer()
|
OpenGLStreamBuffer::~OpenGLStreamBuffer()
|
||||||
{
|
{
|
||||||
glDeleteBuffers(1, &m_buffer_id);
|
glDeleteBuffers(1, &m_buffer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBuffer::Bind()
|
void OpenGLStreamBuffer::Bind()
|
||||||
{
|
{
|
||||||
glBindBuffer(m_target, m_buffer_id);
|
glBindBuffer(m_target, m_buffer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBuffer::Unbind()
|
void OpenGLStreamBuffer::Unbind()
|
||||||
{
|
{
|
||||||
glBindBuffer(m_target, 0);
|
glBindBuffer(m_target, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
void OpenGLStreamBuffer::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (glObjectLabel)
|
||||||
|
{
|
||||||
|
glObjectLabel(GL_BUFFER, GetGLBufferId(), static_cast<GLsizei>(name.length()),
|
||||||
|
static_cast<const GLchar*>(name.data()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
// Uses glBufferSubData() to update. Preferred for drivers which don't support {ARB,EXT}_buffer_storage.
|
// Uses glBufferSubData() to update. Preferred for drivers which don't support {ARB,EXT}_buffer_storage.
|
||||||
class BufferSubDataStreamBuffer final : public StreamBuffer
|
class BufferSubDataStreamBuffer final : public OpenGLStreamBuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~BufferSubDataStreamBuffer() override = default;
|
~BufferSubDataStreamBuffer() override { _aligned_free(m_cpu_buffer); }
|
||||||
|
|
||||||
MappingResult Map(u32 alignment, u32 min_size) override
|
MappingResult Map(u32 alignment, u32 min_size) override
|
||||||
{
|
{
|
||||||
return MappingResult{static_cast<void*>(m_cpu_buffer.data()), 0, 0, m_size / alignment};
|
return MappingResult{static_cast<void*>(m_cpu_buffer), 0, 0, m_size / alignment};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unmap(u32 used_size) override
|
u32 Unmap(u32 used_size) override
|
||||||
{
|
{
|
||||||
if (used_size == 0)
|
if (used_size == 0)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
glBindBuffer(m_target, m_buffer_id);
|
glBindBuffer(m_target, m_buffer_id);
|
||||||
glBufferSubData(m_target, 0, used_size, m_cpu_buffer.data());
|
glBufferSubData(m_target, 0, used_size, m_cpu_buffer);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
|
u32 GetChunkSize() const override { return m_size; }
|
||||||
|
|
||||||
|
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size)
|
||||||
{
|
{
|
||||||
glGetError();
|
glGetError();
|
||||||
|
|
||||||
|
@ -63,43 +77,49 @@ public:
|
||||||
GLenum err = glGetError();
|
GLenum err = glGetError();
|
||||||
if (err != GL_NO_ERROR)
|
if (err != GL_NO_ERROR)
|
||||||
{
|
{
|
||||||
|
glBindBuffer(target, 0);
|
||||||
glDeleteBuffers(1, &buffer_id);
|
glDeleteBuffers(1, &buffer_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::unique_ptr<StreamBuffer>(new BufferSubDataStreamBuffer(target, buffer_id, size));
|
return std::unique_ptr<OpenGLStreamBuffer>(new BufferSubDataStreamBuffer(target, buffer_id, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BufferSubDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
BufferSubDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size) : OpenGLStreamBuffer(target, buffer_id, size)
|
||||||
: StreamBuffer(target, buffer_id, size), m_cpu_buffer(size)
|
|
||||||
{
|
{
|
||||||
|
m_cpu_buffer = static_cast<u8*>(_aligned_malloc(size, 32));
|
||||||
|
if (!m_cpu_buffer)
|
||||||
|
Panic("Failed to allocate CPU storage for GL buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> m_cpu_buffer;
|
u8* m_cpu_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Uses BufferData() to orphan the buffer after every update. Used on Mali where BufferSubData forces a sync.
|
// Uses BufferData() to orphan the buffer after every update. Used on Mali where BufferSubData forces a sync.
|
||||||
class BufferDataStreamBuffer final : public StreamBuffer
|
class BufferDataStreamBuffer final : public OpenGLStreamBuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~BufferDataStreamBuffer() override = default;
|
~BufferDataStreamBuffer() override { _aligned_free(m_cpu_buffer); }
|
||||||
|
|
||||||
MappingResult Map(u32 alignment, u32 min_size) override
|
MappingResult Map(u32 alignment, u32 min_size) override
|
||||||
{
|
{
|
||||||
return MappingResult{static_cast<void*>(m_cpu_buffer.data()), 0, 0, m_size / alignment};
|
return MappingResult{static_cast<void*>(m_cpu_buffer), 0, 0, m_size / alignment};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unmap(u32 used_size) override
|
u32 Unmap(u32 used_size) override
|
||||||
{
|
{
|
||||||
if (used_size == 0)
|
if (used_size == 0)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
glBindBuffer(m_target, m_buffer_id);
|
glBindBuffer(m_target, m_buffer_id);
|
||||||
glBufferData(m_target, used_size, m_cpu_buffer.data(), GL_STREAM_DRAW);
|
glBufferData(m_target, used_size, m_cpu_buffer, GL_STREAM_DRAW);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
|
u32 GetChunkSize() const override { return m_size; }
|
||||||
|
|
||||||
|
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size)
|
||||||
{
|
{
|
||||||
glGetError();
|
glGetError();
|
||||||
|
|
||||||
|
@ -111,24 +131,27 @@ public:
|
||||||
GLenum err = glGetError();
|
GLenum err = glGetError();
|
||||||
if (err != GL_NO_ERROR)
|
if (err != GL_NO_ERROR)
|
||||||
{
|
{
|
||||||
|
glBindBuffer(target, 0);
|
||||||
glDeleteBuffers(1, &buffer_id);
|
glDeleteBuffers(1, &buffer_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::unique_ptr<StreamBuffer>(new BufferDataStreamBuffer(target, buffer_id, size));
|
return std::unique_ptr<OpenGLStreamBuffer>(new BufferDataStreamBuffer(target, buffer_id, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BufferDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
BufferDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size) : OpenGLStreamBuffer(target, buffer_id, size)
|
||||||
: StreamBuffer(target, buffer_id, size), m_cpu_buffer(size)
|
|
||||||
{
|
{
|
||||||
|
m_cpu_buffer = static_cast<u8*>(_aligned_malloc(size, 32));
|
||||||
|
if (!m_cpu_buffer)
|
||||||
|
Panic("Failed to allocate CPU storage for GL buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> m_cpu_buffer;
|
u8* m_cpu_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Base class for implementations which require syncing.
|
// Base class for implementations which require syncing.
|
||||||
class SyncingStreamBuffer : public StreamBuffer
|
class SyncingStreamBuffer : public OpenGLStreamBuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum : u32
|
enum : u32
|
||||||
|
@ -147,13 +170,13 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SyncingStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
SyncingStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||||
: StreamBuffer(target, buffer_id, size), m_bytes_per_block((size + (NUM_SYNC_POINTS)-1) / NUM_SYNC_POINTS)
|
: OpenGLStreamBuffer(target, buffer_id, size), m_bytes_per_block((size + (NUM_SYNC_POINTS)-1) / NUM_SYNC_POINTS)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetSyncIndexForOffset(u32 offset) { return offset / m_bytes_per_block; }
|
ALWAYS_INLINE u32 GetSyncIndexForOffset(u32 offset) { return offset / m_bytes_per_block; }
|
||||||
|
|
||||||
void AddSyncsForOffset(u32 offset)
|
ALWAYS_INLINE void AddSyncsForOffset(u32 offset)
|
||||||
{
|
{
|
||||||
const u32 end = GetSyncIndexForOffset(offset);
|
const u32 end = GetSyncIndexForOffset(offset);
|
||||||
for (; m_used_block_index < end; m_used_block_index++)
|
for (; m_used_block_index < end; m_used_block_index++)
|
||||||
|
@ -163,14 +186,14 @@ protected:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitForSync(GLsync& sync)
|
ALWAYS_INLINE void WaitForSync(GLsync& sync)
|
||||||
{
|
{
|
||||||
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||||
glDeleteSync(sync);
|
glDeleteSync(sync);
|
||||||
sync = nullptr;
|
sync = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureSyncsWaitedForOffset(u32 offset)
|
ALWAYS_INLINE void EnsureSyncsWaitedForOffset(u32 offset)
|
||||||
{
|
{
|
||||||
const u32 end = std::min<u32>(GetSyncIndexForOffset(offset) + 1, NUM_SYNC_POINTS);
|
const u32 end = std::min<u32>(GetSyncIndexForOffset(offset) + 1, NUM_SYNC_POINTS);
|
||||||
for (; m_available_block_index < end; m_available_block_index++)
|
for (; m_available_block_index < end; m_available_block_index++)
|
||||||
|
@ -207,6 +230,8 @@ protected:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetChunkSize() const override { return m_size / NUM_SYNC_POINTS; }
|
||||||
|
|
||||||
u32 m_position = 0;
|
u32 m_position = 0;
|
||||||
u32 m_used_block_index = 0;
|
u32 m_used_block_index = 0;
|
||||||
u32 m_available_block_index = NUM_SYNC_POINTS;
|
u32 m_available_block_index = NUM_SYNC_POINTS;
|
||||||
|
@ -221,6 +246,7 @@ public:
|
||||||
{
|
{
|
||||||
glBindBuffer(m_target, m_buffer_id);
|
glBindBuffer(m_target, m_buffer_id);
|
||||||
glUnmapBuffer(m_target);
|
glUnmapBuffer(m_target);
|
||||||
|
glBindBuffer(m_target, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingResult Map(u32 alignment, u32 min_size) override
|
MappingResult Map(u32 alignment, u32 min_size) override
|
||||||
|
@ -236,19 +262,22 @@ public:
|
||||||
free_space_in_block / alignment};
|
free_space_in_block / alignment};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unmap(u32 used_size) override
|
u32 Unmap(u32 used_size) override
|
||||||
{
|
{
|
||||||
DebugAssert((m_position + used_size) <= m_size);
|
DebugAssert((m_position + used_size) <= m_size);
|
||||||
if (!m_coherent)
|
if (!m_coherent)
|
||||||
{
|
{
|
||||||
|
// TODO: shouldn't be needed anymore
|
||||||
Bind();
|
Bind();
|
||||||
glFlushMappedBufferRange(m_target, m_position, used_size);
|
glFlushMappedBufferRange(m_target, m_position, used_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const u32 prev_position = m_position;
|
||||||
m_position += used_size;
|
m_position += used_size;
|
||||||
|
return prev_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size, bool coherent = true)
|
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size, bool coherent = true)
|
||||||
{
|
{
|
||||||
glGetError();
|
glGetError();
|
||||||
|
|
||||||
|
@ -266,14 +295,16 @@ public:
|
||||||
GLenum err = glGetError();
|
GLenum err = glGetError();
|
||||||
if (err != GL_NO_ERROR)
|
if (err != GL_NO_ERROR)
|
||||||
{
|
{
|
||||||
|
glBindBuffer(target, 0);
|
||||||
glDeleteBuffers(1, &buffer_id);
|
glDeleteBuffers(1, &buffer_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* mapped_ptr = static_cast<u8*>(glMapBufferRange(target, 0, size, map_flags));
|
u8* mapped_ptr = static_cast<u8*>(glMapBufferRange(target, 0, size, map_flags));
|
||||||
Assert(mapped_ptr);
|
AssertMsg(mapped_ptr, "Persistent buffer was mapped");
|
||||||
|
|
||||||
return std::unique_ptr<StreamBuffer>(new BufferStorageStreamBuffer(target, buffer_id, size, mapped_ptr, coherent));
|
return std::unique_ptr<OpenGLStreamBuffer>(
|
||||||
|
new BufferStorageStreamBuffer(target, buffer_id, size, mapped_ptr, coherent));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -286,14 +317,14 @@ private:
|
||||||
bool m_coherent;
|
bool m_coherent;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<StreamBuffer> StreamBuffer::Create(GLenum target, u32 size)
|
std::unique_ptr<OpenGLStreamBuffer> OpenGLStreamBuffer::Create(GLenum target, u32 size)
|
||||||
{
|
{
|
||||||
std::unique_ptr<StreamBuffer> buf;
|
std::unique_ptr<OpenGLStreamBuffer> buf;
|
||||||
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage)
|
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage)
|
||||||
{
|
{
|
||||||
buf = detail::BufferStorageStreamBuffer::Create(target, size);
|
buf = BufferStorageStreamBuffer::Create(target, size);
|
||||||
if (buf)
|
if (buf)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
@ -304,13 +335,11 @@ std::unique_ptr<StreamBuffer> StreamBuffer::Create(GLenum target, u32 size)
|
||||||
if (std::strcmp(vendor, "ARM") == 0 || std::strcmp(vendor, "Qualcomm") == 0)
|
if (std::strcmp(vendor, "ARM") == 0 || std::strcmp(vendor, "Qualcomm") == 0)
|
||||||
{
|
{
|
||||||
// Mali and Adreno drivers can't do sub-buffer tracking...
|
// Mali and Adreno drivers can't do sub-buffer tracking...
|
||||||
return detail::BufferDataStreamBuffer::Create(target, size);
|
return BufferDataStreamBuffer::Create(target, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return detail::BufferSubDataStreamBuffer::Create(target, size);
|
return BufferSubDataStreamBuffer::Create(target, size);
|
||||||
#else
|
#else
|
||||||
return detail::BufferDataStreamBuffer::Create(target, size);
|
return BufferDataStreamBuffer::Create(target, size);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace GL
|
|
|
@ -2,17 +2,20 @@
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "opengl_loader.h"
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "loader.h"
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace GL {
|
class OpenGLStreamBuffer
|
||||||
class StreamBuffer
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~StreamBuffer();
|
virtual ~OpenGLStreamBuffer();
|
||||||
|
|
||||||
ALWAYS_INLINE GLuint GetGLBufferId() const { return m_buffer_id; }
|
ALWAYS_INLINE GLuint GetGLBufferId() const { return m_buffer_id; }
|
||||||
ALWAYS_INLINE GLenum GetGLTarget() const { return m_target; }
|
ALWAYS_INLINE GLenum GetGLTarget() const { return m_target; }
|
||||||
|
@ -21,6 +24,8 @@ public:
|
||||||
void Bind();
|
void Bind();
|
||||||
void Unbind();
|
void Unbind();
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name);
|
||||||
|
|
||||||
struct MappingResult
|
struct MappingResult
|
||||||
{
|
{
|
||||||
void* pointer;
|
void* pointer;
|
||||||
|
@ -30,15 +35,19 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual MappingResult Map(u32 alignment, u32 min_size) = 0;
|
virtual MappingResult Map(u32 alignment, u32 min_size) = 0;
|
||||||
virtual void Unmap(u32 used_size) = 0;
|
|
||||||
|
|
||||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size);
|
/// Returns the position in the buffer *before* the start of used_size.
|
||||||
|
virtual u32 Unmap(u32 used_size) = 0;
|
||||||
|
|
||||||
|
/// Returns the minimum granularity of blocks which sync objects will be created around.
|
||||||
|
virtual u32 GetChunkSize() const = 0;
|
||||||
|
|
||||||
|
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
StreamBuffer(GLenum target, GLuint buffer_id, u32 size);
|
OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size);
|
||||||
|
|
||||||
GLenum m_target;
|
GLenum m_target;
|
||||||
GLuint m_buffer_id;
|
GLuint m_buffer_id;
|
||||||
u32 m_size;
|
u32 m_size;
|
||||||
};
|
};
|
||||||
} // namespace GL
|
|
|
@ -0,0 +1,739 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#include "opengl_texture.h"
|
||||||
|
#include "opengl_device.h"
|
||||||
|
#include "opengl_stream_buffer.h"
|
||||||
|
|
||||||
|
#include "common/align.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
Log_SetChannel(OpenGLDevice);
|
||||||
|
|
||||||
|
// Looking across a range of GPUs, the optimal copy alignment for Vulkan drivers seems
|
||||||
|
// to be between 1 (AMD/NV) and 64 (Intel). So, we'll go with 64 here.
|
||||||
|
static constexpr u32 TEXTURE_UPLOAD_ALIGNMENT = 64;
|
||||||
|
|
||||||
|
// The pitch alignment must be less or equal to the upload alignment.
|
||||||
|
// We need 32 here for AVX2, so 64 is also fine.
|
||||||
|
static constexpr u32 TEXTURE_UPLOAD_PITCH_ALIGNMENT = 64;
|
||||||
|
|
||||||
|
bool OpenGLTexture::s_use_pbo_for_uploads = false;
|
||||||
|
|
||||||
|
const std::tuple<GLenum, GLenum, GLenum>& OpenGLTexture::GetPixelFormatMapping(GPUTexture::Format format)
|
||||||
|
{
|
||||||
|
static constexpr std::array<std::tuple<GLenum, GLenum, GLenum>, static_cast<u32>(GPUTexture::Format::Count)> mapping =
|
||||||
|
{{
|
||||||
|
{}, // Unknown
|
||||||
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
|
||||||
|
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8
|
||||||
|
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
||||||
|
{GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGBA5551
|
||||||
|
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8
|
||||||
|
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16
|
||||||
|
}};
|
||||||
|
|
||||||
|
return mapping[static_cast<u32>(format)];
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLTexture::OpenGLTexture() = default;
|
||||||
|
|
||||||
|
OpenGLTexture::~OpenGLTexture()
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLTexture::UseTextureStorage(bool multisampled)
|
||||||
|
{
|
||||||
|
return GLAD_GL_ARB_texture_storage || (multisampled ? GLAD_GL_ES_VERSION_3_1 : GLAD_GL_ES_VERSION_3_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLTexture::UseTextureStorage() const
|
||||||
|
{
|
||||||
|
return UseTextureStorage(IsMultisampled());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Format format, const void* data,
|
||||||
|
u32 data_pitch)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
|
||||||
|
if (width > MAX_WIDTH || height > MAX_HEIGHT || layers > MAX_LAYERS || levels > MAX_LEVELS || samples > MAX_SAMPLES)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Invalid dimensions: %ux%ux%u %u %u", width, height, layers, levels, samples);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (samples > 1 && levels > 1)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Multisampled textures can't have mip levels");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layers > 1 && data)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Loading texture array data not currently supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLenum target = ((samples > 1) ? ((layers > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D_MULTISAMPLE_ARRAY) :
|
||||||
|
((layers > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
|
||||||
|
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(format);
|
||||||
|
|
||||||
|
OpenGLDevice::BindUpdateTextureUnit();
|
||||||
|
|
||||||
|
GLuint id;
|
||||||
|
glGenTextures(1, &id);
|
||||||
|
glBindTexture(target, id);
|
||||||
|
|
||||||
|
if (samples > 1)
|
||||||
|
{
|
||||||
|
Assert(!data);
|
||||||
|
if (UseTextureStorage(true))
|
||||||
|
{
|
||||||
|
if (layers > 1)
|
||||||
|
glTexStorage3DMultisample(target, samples, gl_internal_format, width, height, layers, GL_FALSE);
|
||||||
|
else
|
||||||
|
glTexStorage2DMultisample(target, samples, gl_internal_format, width, height, GL_FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (layers > 1)
|
||||||
|
glTexImage3DMultisample(target, samples, gl_internal_format, width, height, layers, GL_FALSE);
|
||||||
|
else
|
||||||
|
glTexImage2DMultisample(target, samples, gl_internal_format, width, height, GL_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
|
||||||
|
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (UseTextureStorage(false))
|
||||||
|
{
|
||||||
|
if (layers > 1)
|
||||||
|
glTexStorage3D(target, levels, gl_internal_format, width, height, layers);
|
||||||
|
else
|
||||||
|
glTexStorage2D(target, levels, gl_internal_format, width, height);
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
// TODO: Fix data for mipmaps here.
|
||||||
|
if (layers > 1)
|
||||||
|
glTexSubImage3D(target, 0, 0, 0, 0, width, height, layers, gl_format, gl_type, data);
|
||||||
|
else
|
||||||
|
glTexSubImage2D(target, 0, 0, 0, width, height, gl_format, gl_type, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < levels; i++)
|
||||||
|
{
|
||||||
|
// TODO: Fix data pointer here.
|
||||||
|
if (layers > 1)
|
||||||
|
glTexImage3D(target, i, gl_internal_format, width, height, layers, 0, gl_format, gl_type, data);
|
||||||
|
else
|
||||||
|
glTexImage2D(target, i, gl_internal_format, width, height, 0, gl_format, gl_type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
|
||||||
|
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum error = glGetError();
|
||||||
|
if (error != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create texture: 0x%X", error);
|
||||||
|
glDeleteTextures(1, &id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsValid())
|
||||||
|
Destroy();
|
||||||
|
|
||||||
|
m_id = id;
|
||||||
|
m_width = static_cast<u16>(width);
|
||||||
|
m_height = static_cast<u16>(height);
|
||||||
|
m_layers = static_cast<u8>(layers);
|
||||||
|
m_levels = static_cast<u8>(levels);
|
||||||
|
m_samples = static_cast<u8>(samples);
|
||||||
|
m_format = format;
|
||||||
|
m_state = GPUTexture::State::Dirty;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void OpenGLTexture::Replace(u32 width, u32 height, GLenum internal_format, GLenum format, GLenum type, const void* data)
|
||||||
|
{
|
||||||
|
Assert(IsValid() && width < MAX_WIDTH && height < MAX_HEIGHT && m_layers == 1 && m_samples == 1 && m_levels == 1);
|
||||||
|
|
||||||
|
const bool size_changed = (width != m_width || height != m_height);
|
||||||
|
|
||||||
|
m_width = static_cast<u16>(width);
|
||||||
|
m_height = static_cast<u16>(height);
|
||||||
|
m_levels = 1;
|
||||||
|
|
||||||
|
const GLenum target = GetGLTarget();
|
||||||
|
glBindTexture(target, m_id);
|
||||||
|
|
||||||
|
if (UseTextureStorage())
|
||||||
|
{
|
||||||
|
if (size_changed)
|
||||||
|
{
|
||||||
|
if (m_layers > 0)
|
||||||
|
glTexStorage3D(target, m_levels, internal_format, m_width, m_height, m_levels);
|
||||||
|
else
|
||||||
|
glTexStorage2D(target, m_levels, internal_format, m_width, m_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
glTexSubImage2D(target, 0, 0, 0, m_width, m_height, format, type, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glTexImage2D(target, 0, internal_format, width, height, 0, format, type, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLTexture::ReplaceImage(u32 layer, u32 level, GLenum format, GLenum type, const void* data)
|
||||||
|
{
|
||||||
|
Assert(IsValid() && !IsMultisampled());
|
||||||
|
|
||||||
|
const GLenum target = GetGLTarget();
|
||||||
|
if (IsTextureArray())
|
||||||
|
glTexSubImage3D(target, level, 0, 0, layer, m_width, m_height, 1, format, type, data);
|
||||||
|
else
|
||||||
|
glTexSubImage2D(target, level, 0, 0, m_width, m_height, format, type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLTexture::ReplaceSubImage(u32 layer, u32 level, u32 x, u32 y, u32 width, u32 height, GLenum format,
|
||||||
|
GLenum type, const void* data)
|
||||||
|
{
|
||||||
|
Assert(IsValid() && !IsMultisampled());
|
||||||
|
|
||||||
|
const GLenum target = GetGLTarget();
|
||||||
|
if (IsTextureArray())
|
||||||
|
glTexSubImage3D(target, level, x, y, layer, width, height, 1, format, type, data);
|
||||||
|
else
|
||||||
|
glTexSubImage2D(target, level, x, y, width, height, format, type, data);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void OpenGLTexture::Destroy()
|
||||||
|
{
|
||||||
|
if (m_id != 0)
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetInstance().UnbindTexture(m_id);
|
||||||
|
glDeleteTextures(1, &m_id);
|
||||||
|
m_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearBaseProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLTexture::CommitClear()
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetInstance().CommitClear(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/,
|
||||||
|
u32 level /*= 0*/)
|
||||||
|
{
|
||||||
|
// TODO: perf counters
|
||||||
|
|
||||||
|
// Worth using the PBO? Driver probably knows better...
|
||||||
|
const GLenum target = GetGLTarget();
|
||||||
|
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_format);
|
||||||
|
const u32 preferred_pitch =
|
||||||
|
Common::AlignUpPow2(static_cast<u32>(width) * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
||||||
|
const u32 map_size = preferred_pitch * static_cast<u32>(height);
|
||||||
|
OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer();
|
||||||
|
|
||||||
|
CommitClear();
|
||||||
|
|
||||||
|
OpenGLDevice::BindUpdateTextureUnit();
|
||||||
|
glBindTexture(target, m_id);
|
||||||
|
|
||||||
|
if (!sb || map_size > sb->GetChunkSize())
|
||||||
|
{
|
||||||
|
GL_INS("Not using PBO for map size %u", map_size);
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / GetPixelSize());
|
||||||
|
glTextureSubImage2D(target, layer, x, y, width, height, gl_format, gl_type, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto map = sb->Map(TEXTURE_UPLOAD_ALIGNMENT, map_size);
|
||||||
|
StringUtil::StrideMemCpy(map.pointer, preferred_pitch, data, pitch, width * GetPixelSize(), height);
|
||||||
|
sb->Unmap(map_size);
|
||||||
|
sb->Bind();
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, preferred_pitch / GetPixelSize());
|
||||||
|
glTextureSubImage2D(GL_TEXTURE_2D, layer, x, y, width, height, gl_format, gl_type,
|
||||||
|
reinterpret_cast<void*>(static_cast<uintptr_t>(map.buffer_offset)));
|
||||||
|
|
||||||
|
sb->Unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(target, 0);
|
||||||
|
|
||||||
|
UnreachableCode();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLTexture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
|
||||||
|
u32 level /*= 0*/)
|
||||||
|
{
|
||||||
|
if ((x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || layer > m_layers || level > m_levels)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const u32 pitch = Common::AlignUpPow2(static_cast<u32>(width) * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
||||||
|
const u32 upload_size = pitch * static_cast<u32>(height);
|
||||||
|
OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer();
|
||||||
|
if (!sb || upload_size > sb->GetSize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto res = sb->Map(TEXTURE_UPLOAD_ALIGNMENT, upload_size);
|
||||||
|
*map = res.pointer;
|
||||||
|
*map_stride = pitch;
|
||||||
|
|
||||||
|
m_map_offset = res.buffer_offset;
|
||||||
|
m_map_x = static_cast<u16>(x);
|
||||||
|
m_map_y = static_cast<u16>(y);
|
||||||
|
m_map_width = static_cast<u16>(width);
|
||||||
|
m_map_height = static_cast<u16>(height);
|
||||||
|
m_map_layer = static_cast<u8>(layer);
|
||||||
|
m_map_level = static_cast<u8>(level);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLTexture::Unmap()
|
||||||
|
{
|
||||||
|
CommitClear();
|
||||||
|
|
||||||
|
const u32 pitch = Common::AlignUpPow2(static_cast<u32>(m_map_width) * GetPixelSize(), TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
||||||
|
const u32 upload_size = pitch * static_cast<u32>(m_map_height);
|
||||||
|
OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer();
|
||||||
|
sb->Unmap(upload_size);
|
||||||
|
sb->Bind();
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_map_width);
|
||||||
|
|
||||||
|
OpenGLDevice::BindUpdateTextureUnit();
|
||||||
|
|
||||||
|
const GLenum target = GetGLTarget();
|
||||||
|
glBindTexture(target, m_id);
|
||||||
|
|
||||||
|
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_format);
|
||||||
|
if (IsTextureArray())
|
||||||
|
{
|
||||||
|
glTexSubImage3D(target, m_map_level, m_map_x, m_map_y, m_map_layer, m_map_width, m_map_height, 1, gl_format,
|
||||||
|
gl_type, reinterpret_cast<void*>(static_cast<uintptr_t>(m_map_offset)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glTexSubImage2D(target, m_map_level, m_map_x, m_map_y, m_map_width, m_map_height, gl_format, gl_type,
|
||||||
|
reinterpret_cast<void*>(static_cast<uintptr_t>(m_map_offset)));
|
||||||
|
}
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
|
||||||
|
sb->Unbind();
|
||||||
|
|
||||||
|
glBindTexture(target, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLTexture::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (glObjectLabel)
|
||||||
|
glObjectLabel(GL_TEXTURE, m_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// If we don't have border clamp.. too bad, just hope for the best.
|
||||||
|
if (!m_gl_context->IsGLES() || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_NV_texture_border_clamp ||
|
||||||
|
GLAD_GL_EXT_texture_border_clamp || GLAD_GL_OES_texture_border_clamp)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
OpenGLSampler::OpenGLSampler(GLuint id) : GPUSampler(), m_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLSampler::~OpenGLSampler()
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetInstance().UnbindSampler(m_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLSampler::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (glObjectLabel)
|
||||||
|
glObjectLabel(GL_SAMPLER, m_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSampler> OpenGLDevice::CreateSampler(const GPUSampler::Config& config)
|
||||||
|
{
|
||||||
|
static constexpr std::array<GLenum, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{
|
||||||
|
GL_REPEAT, // Repeat
|
||||||
|
GL_CLAMP_TO_EDGE, // ClampToEdge
|
||||||
|
GL_CLAMP_TO_BORDER, // ClampToBorder
|
||||||
|
}};
|
||||||
|
|
||||||
|
// [mipmap_on_off][mipmap][filter]
|
||||||
|
static constexpr GLenum filters[2][2][2] = {
|
||||||
|
{
|
||||||
|
// mipmap=off
|
||||||
|
{GL_NEAREST, GL_LINEAR}, // mipmap=nearest
|
||||||
|
{GL_NEAREST, GL_LINEAR}, // mipmap=linear
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// mipmap=on
|
||||||
|
{GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST}, // mipmap=nearest
|
||||||
|
{GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR}, // mipmap=linear
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
GLuint sampler;
|
||||||
|
glGetError();
|
||||||
|
glGenSamplers(1, &sampler);
|
||||||
|
if (glGetError() != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create sampler: %u", sampler);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, ta[static_cast<u8>(config.address_u.GetValue())]);
|
||||||
|
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, ta[static_cast<u8>(config.address_v.GetValue())]);
|
||||||
|
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, ta[static_cast<u8>(config.address_w.GetValue())]);
|
||||||
|
const u8 mipmap_on_off = (config.min_lod != 0 || config.max_lod != 0);
|
||||||
|
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER,
|
||||||
|
filters[mipmap_on_off][static_cast<u8>(config.mip_filter.GetValue())]
|
||||||
|
[static_cast<u8>(config.min_filter.GetValue())]);
|
||||||
|
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER,
|
||||||
|
filters[mipmap_on_off][static_cast<u8>(config.mip_filter.GetValue())]
|
||||||
|
[static_cast<u8>(config.mag_filter.GetValue())]);
|
||||||
|
glSamplerParameterf(sampler, GL_TEXTURE_MIN_LOD, static_cast<float>(config.min_lod));
|
||||||
|
glSamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, static_cast<float>(config.max_lod));
|
||||||
|
glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, config.GetBorderFloatColor().data());
|
||||||
|
if (config.anisotropy)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unique_ptr<GPUSampler>(new OpenGLSampler(sampler));
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
OpenGLFramebuffer::OpenGLFramebuffer(GPUTexture* rt, GPUTexture* ds, u32 width, u32 height, GLuint id)
|
||||||
|
: GPUFramebuffer(rt, ds, width, height), m_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLFramebuffer::~OpenGLFramebuffer()
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetInstance().UnbindFramebuffer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLFramebuffer::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (glObjectLabel)
|
||||||
|
glObjectLabel(GL_FRAMEBUFFER, m_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLFramebuffer::Bind(GLenum target)
|
||||||
|
{
|
||||||
|
glBindFramebuffer(target, m_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUFramebuffer> OpenGLDevice::CreateFramebuffer(GPUTexture* rt, u32 rt_layer, u32 rt_level,
|
||||||
|
GPUTexture* ds, u32 ds_layer, u32 ds_level)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
|
||||||
|
GLuint fbo_id;
|
||||||
|
glGenFramebuffers(1, &fbo_id);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id);
|
||||||
|
|
||||||
|
OpenGLTexture* RT = static_cast<OpenGLTexture*>(rt);
|
||||||
|
OpenGLTexture* DS = static_cast<OpenGLTexture*>(ds);
|
||||||
|
if (RT)
|
||||||
|
{
|
||||||
|
if (RT->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, fbo_id, RT->GetGLId(), rt_level, rt_layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, RT->GetGLId(), rt_level);
|
||||||
|
}
|
||||||
|
if (DS)
|
||||||
|
{
|
||||||
|
if (DS->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, fbo_id, DS->GetGLId(), rt_level, rt_layer);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, DS->GetGLId(), rt_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR || glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create GL framebuffer: %u", glGetError());
|
||||||
|
glDeleteFramebuffers(1, &fbo_id);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_current_framebuffer ? m_current_framebuffer->GetGLId() : 0);
|
||||||
|
return std::unique_ptr<GPUFramebuffer>(
|
||||||
|
new OpenGLFramebuffer(rt, ds, rt ? rt->GetMipWidth(rt_level) : ds->GetMipWidth(ds_level),
|
||||||
|
rt ? rt->GetMipHeight(rt_level) : ds->GetMipHeight(ds_level), fbo_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::CommitClear(OpenGLTexture* tex)
|
||||||
|
{
|
||||||
|
switch (tex->GetState())
|
||||||
|
{
|
||||||
|
case GPUTexture::State::Invalidated:
|
||||||
|
{
|
||||||
|
tex->SetState(GPUTexture::State::Dirty);
|
||||||
|
|
||||||
|
if (glInvalidateTexImage)
|
||||||
|
{
|
||||||
|
glInvalidateTexImage(tex->GetGLId(), 0);
|
||||||
|
}
|
||||||
|
else if (glInvalidateFramebuffer)
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_write_fbo);
|
||||||
|
|
||||||
|
const GLenum attachment = tex->IsDepthStencil() ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0;
|
||||||
|
if (tex->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, tex->GetGLId(), 0, 0);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex->GetGLId(), 0);
|
||||||
|
|
||||||
|
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_current_framebuffer ? m_current_framebuffer->GetGLId() : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPUTexture::State::Cleared:
|
||||||
|
{
|
||||||
|
tex->SetState(GPUTexture::State::Dirty);
|
||||||
|
|
||||||
|
if (glClearTexImage)
|
||||||
|
{
|
||||||
|
const auto [gl_internal_format, gl_format, gl_type] = OpenGLTexture::GetPixelFormatMapping(tex->GetFormat());
|
||||||
|
glClearTexImage(tex->GetGLId(), 0, gl_format, gl_type, &tex->GetClearValue());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_write_fbo);
|
||||||
|
|
||||||
|
const GLenum attachment = tex->IsDepthStencil() ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0;
|
||||||
|
if (tex->IsTextureArray())
|
||||||
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, tex->GetGLId(), 0, 0);
|
||||||
|
else
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex->GetGLId(), 0);
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
if (tex->IsDepthStencil())
|
||||||
|
{
|
||||||
|
glClearDepth(tex->GetClearDepth());
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto color = tex->GetUNormClearColor();
|
||||||
|
glClearColor(color[0], color[1], color[2], color[3]);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_current_framebuffer ? m_current_framebuffer->GetGLId() : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPUTexture::State::Dirty:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::CommitClear(OpenGLFramebuffer* fb)
|
||||||
|
{
|
||||||
|
GLenum clear_flags = 0;
|
||||||
|
GLenum invalidate_attachments[2];
|
||||||
|
GLuint num_invalidate_attachments = 0;
|
||||||
|
|
||||||
|
if (OpenGLTexture* FB = static_cast<OpenGLTexture*>(fb->GetRT()))
|
||||||
|
{
|
||||||
|
switch (FB->GetState())
|
||||||
|
{
|
||||||
|
case GPUTexture::State::Invalidated:
|
||||||
|
{
|
||||||
|
invalidate_attachments[num_invalidate_attachments++] = GL_COLOR_ATTACHMENT0;
|
||||||
|
FB->SetState(GPUTexture::State::Dirty);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPUTexture::State::Cleared:
|
||||||
|
{
|
||||||
|
const auto color = FB->GetUNormClearColor();
|
||||||
|
glClearColor(color[0], color[1], color[2], color[3]);
|
||||||
|
clear_flags |= GL_COLOR_BUFFER_BIT;
|
||||||
|
FB->SetState(GPUTexture::State::Dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
case GPUTexture::State::Dirty:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (OpenGLTexture* DS = static_cast<OpenGLTexture*>(fb->GetDS()))
|
||||||
|
{
|
||||||
|
switch (DS->GetState())
|
||||||
|
{
|
||||||
|
case GPUTexture::State::Invalidated:
|
||||||
|
{
|
||||||
|
invalidate_attachments[num_invalidate_attachments++] = GL_DEPTH_ATTACHMENT;
|
||||||
|
DS->SetState(GPUTexture::State::Dirty);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPUTexture::State::Cleared:
|
||||||
|
{
|
||||||
|
glClearDepth(DS->GetClearDepth());
|
||||||
|
clear_flags |= GL_DEPTH_BUFFER_BIT;
|
||||||
|
DS->SetState(GPUTexture::State::Dirty);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPUTexture::State::Dirty:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear_flags != 0)
|
||||||
|
{
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glClear(clear_flags);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
if (num_invalidate_attachments > 0 && glInvalidateFramebuffer)
|
||||||
|
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, num_invalidate_attachments, invalidate_attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
OpenGLTextureBuffer::OpenGLTextureBuffer(Format format, u32 size_in_elements,
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> buffer, GLuint texture_id)
|
||||||
|
: GPUTextureBuffer(format, size_in_elements), m_buffer(std::move(buffer)), m_texture_id(texture_id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLTextureBuffer::~OpenGLTextureBuffer()
|
||||||
|
{
|
||||||
|
// TODO: unbind ssbo
|
||||||
|
if (m_texture_id != 0)
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetInstance().UnbindTexture(m_texture_id);
|
||||||
|
glDeleteTextures(1, &m_texture_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLTextureBuffer::CreateBuffer()
|
||||||
|
{
|
||||||
|
const bool use_ssbo = OpenGLDevice::GetInstance().GetFeatures().texture_buffers_emulated_with_ssbo;
|
||||||
|
|
||||||
|
const GLenum target = (use_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_TEXTURE_BUFFER);
|
||||||
|
m_buffer = OpenGLStreamBuffer::Create(target, GetSizeInBytes());
|
||||||
|
if (!m_buffer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!use_ssbo)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
glGenTextures(1, &m_texture_id);
|
||||||
|
if (const GLenum err = glGetError(); err != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create texture for buffer: %u", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLDevice::BindUpdateTextureUnit();
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, m_texture_id);
|
||||||
|
glTexBuffer(GL_TEXTURE_BUFFER, GL_R16UI, m_buffer->GetGLBufferId());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer->Unbind();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLTextureBuffer::Map(u32 required_elements)
|
||||||
|
{
|
||||||
|
const u32 esize = GetElementSize(m_format);
|
||||||
|
const auto map = m_buffer->Map(esize, esize * required_elements);
|
||||||
|
m_current_position = map.index_aligned;
|
||||||
|
return map.pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLTextureBuffer::Unmap(u32 used_elements)
|
||||||
|
{
|
||||||
|
m_buffer->Unmap(used_elements * GetElementSize(m_format));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUTextureBuffer> OpenGLDevice::CreateTextureBuffer(GPUTextureBuffer::Format format,
|
||||||
|
u32 size_in_elements)
|
||||||
|
{
|
||||||
|
const bool use_ssbo = OpenGLDevice::GetInstance().GetFeatures().texture_buffers_emulated_with_ssbo;
|
||||||
|
|
||||||
|
const GLenum target = (use_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_TEXTURE_BUFFER);
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> buffer =
|
||||||
|
OpenGLStreamBuffer::Create(target, GPUTextureBuffer::GetElementSize(format) * size_in_elements);
|
||||||
|
if (!buffer)
|
||||||
|
return {};
|
||||||
|
buffer->Unbind();
|
||||||
|
|
||||||
|
GLuint texture_id = 0;
|
||||||
|
if (!use_ssbo)
|
||||||
|
{
|
||||||
|
glGetError();
|
||||||
|
glGenTextures(1, &texture_id);
|
||||||
|
if (const GLenum err = glGetError(); err != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create texture for buffer: %u", err);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLDevice::BindUpdateTextureUnit();
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, texture_id);
|
||||||
|
glTexBuffer(GL_TEXTURE_BUFFER, GL_R16UI, buffer->GetGLBufferId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unique_ptr<GPUTextureBuffer>(
|
||||||
|
new OpenGLTextureBuffer(format, size_in_elements, std::move(buffer), texture_id));
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "gpu_device.h"
|
||||||
|
#include "gpu_texture.h"
|
||||||
|
#include "opengl_loader.h"
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class OpenGLDevice;
|
||||||
|
class OpenGLStreamBuffer;
|
||||||
|
|
||||||
|
class OpenGLTexture final : public GPUTexture
|
||||||
|
{
|
||||||
|
friend OpenGLDevice;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OpenGLTexture();
|
||||||
|
OpenGLTexture(const OpenGLTexture&) = delete;
|
||||||
|
~OpenGLTexture();
|
||||||
|
|
||||||
|
static bool UseTextureStorage(bool multisampled);
|
||||||
|
static const std::tuple<GLenum, GLenum, GLenum>& GetPixelFormatMapping(Format format);
|
||||||
|
|
||||||
|
ALWAYS_INLINE GLuint GetGLId() const { return m_id; }
|
||||||
|
bool IsValid() const override { return m_id != 0; }
|
||||||
|
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
|
||||||
|
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
||||||
|
void Unmap() override;
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
|
||||||
|
bool Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Format format, const void* data = nullptr,
|
||||||
|
u32 data_pitch = 0);
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
bool UseTextureStorage() const;
|
||||||
|
|
||||||
|
ALWAYS_INLINE GLenum GetGLTarget() const
|
||||||
|
{
|
||||||
|
return (IsMultisampled() ? (IsTextureArray() ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D_MULTISAMPLE_ARRAY) :
|
||||||
|
(IsTextureArray() ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommitClear();
|
||||||
|
|
||||||
|
OpenGLTexture& operator=(const OpenGLTexture&) = delete;
|
||||||
|
|
||||||
|
static bool s_use_pbo_for_uploads;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint m_id = 0;
|
||||||
|
|
||||||
|
u32 m_map_offset = 0;
|
||||||
|
u16 m_map_x = 0;
|
||||||
|
u16 m_map_y = 0;
|
||||||
|
u16 m_map_width = 0;
|
||||||
|
u16 m_map_height = 0;
|
||||||
|
u8 m_map_layer = 0;
|
||||||
|
u8 m_map_level = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenGLFramebuffer final : public GPUFramebuffer
|
||||||
|
{
|
||||||
|
friend OpenGLDevice;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~OpenGLFramebuffer() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE GLuint GetGLId() const { return m_id; }
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
|
||||||
|
void Bind(GLenum target);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenGLFramebuffer(GPUTexture* rt, GPUTexture* ds, u32 width, u32 height, GLuint id);
|
||||||
|
|
||||||
|
GLuint m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenGLTextureBuffer final : public GPUTextureBuffer
|
||||||
|
{
|
||||||
|
friend OpenGLDevice;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~OpenGLTextureBuffer() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE OpenGLStreamBuffer* GetBuffer() const { return m_buffer.get(); }
|
||||||
|
ALWAYS_INLINE GLuint GetTextureId() const { return m_texture_id; }
|
||||||
|
|
||||||
|
bool CreateBuffer();
|
||||||
|
|
||||||
|
// Inherited via GPUTextureBuffer
|
||||||
|
virtual void* Map(u32 required_elements) override;
|
||||||
|
virtual void Unmap(u32 used_elements) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenGLTextureBuffer(Format format, u32 size_in_elements, std::unique_ptr<OpenGLStreamBuffer> buffer,
|
||||||
|
GLuint texture_id);
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLStreamBuffer> m_buffer;
|
||||||
|
GLuint m_texture_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenGLSampler final : public GPUSampler
|
||||||
|
{
|
||||||
|
friend OpenGLDevice;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~OpenGLSampler() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE GLuint GetID() const { return m_id; }
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenGLSampler(GLuint id);
|
||||||
|
|
||||||
|
GLuint m_id;
|
||||||
|
};
|
|
@ -243,7 +243,7 @@ bool PostProcessingChain::CheckTargets(GPUTexture::Format format, u32 target_wid
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width,
|
bool PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width,
|
||||||
s32 final_height, s32 orig_width, s32 orig_height, u32 target_width, u32 target_height)
|
s32 final_height, s32 orig_width, s32 orig_height, u32 target_width, u32 target_height)
|
||||||
{
|
{
|
||||||
const u32 window_width = final_target ? final_target->GetWidth() : g_host_display->GetWindowWidth();
|
const u32 window_width = final_target ? final_target->GetWidth() : g_host_display->GetWindowWidth();
|
||||||
|
@ -256,6 +256,7 @@ void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s3
|
||||||
GPUTexture* input = m_input_texture.get();
|
GPUTexture* input = m_input_texture.get();
|
||||||
input->MakeReadyForSampling();
|
input->MakeReadyForSampling();
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
const PostProcessingShader& final_stage = m_shaders.back();
|
const PostProcessingShader& final_stage = m_shaders.back();
|
||||||
for (PostProcessingShader& stage : m_shaders)
|
for (PostProcessingShader& stage : m_shaders)
|
||||||
{
|
{
|
||||||
|
@ -267,7 +268,18 @@ void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s3
|
||||||
if (!is_final)
|
if (!is_final)
|
||||||
g_host_display->ClearRenderTarget(stage.GetOutputTexture(), 0);
|
g_host_display->ClearRenderTarget(stage.GetOutputTexture(), 0);
|
||||||
|
|
||||||
g_host_display->SetFramebuffer(is_final ? final_target : stage.GetOutputFramebuffer());
|
if (is_final && !final_target)
|
||||||
|
{
|
||||||
|
if (!g_host_display->BeginPresent(false))
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_host_display->SetFramebuffer(is_final ? final_target : stage.GetOutputFramebuffer());
|
||||||
|
}
|
||||||
|
|
||||||
g_host_display->SetPipeline(stage.GetPipeline());
|
g_host_display->SetPipeline(stage.GetPipeline());
|
||||||
g_host_display->SetTextureSampler(0, input, m_border_sampler.get());
|
g_host_display->SetTextureSampler(0, input, m_border_sampler.get());
|
||||||
|
@ -288,4 +300,5 @@ void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s3
|
||||||
}
|
}
|
||||||
|
|
||||||
GL_POP();
|
GL_POP();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ public:
|
||||||
|
|
||||||
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height);
|
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height);
|
||||||
|
|
||||||
void Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
bool Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
||||||
s32 orig_width, s32 orig_height, u32 target_width, u32 target_height);
|
s32 orig_width, s32 orig_height, u32 target_width, u32 target_height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -71,6 +71,16 @@ bool Vulkan::Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* da
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Vulkan::Texture::SetDebugName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vulkan::Texture::MakeReadyForSampling()
|
||||||
|
{
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
|
||||||
bool Vulkan::Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
|
bool Vulkan::Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
|
||||||
u32 level /*= 0*/)
|
u32 level /*= 0*/)
|
||||||
{
|
{
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
void EndUpdate(u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
|
void EndUpdate(u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
|
||||||
bool Update(u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer, const void* data, u32 data_pitch);
|
bool Update(u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer, const void* data, u32 data_pitch);
|
||||||
|
|
||||||
|
void SetDebugName(const std::string_view& name) override;
|
||||||
|
void MakeReadyForSampling() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkImageViewType m_view_type = VK_IMAGE_VIEW_TYPE_2D;
|
VkImageViewType m_view_type = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
|
|
@ -593,6 +593,7 @@ bool VulkanGPUDevice::DoneCurrent()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
bool VulkanGPUDevice::Render(bool skip_present)
|
bool VulkanGPUDevice::Render(bool skip_present)
|
||||||
{
|
{
|
||||||
if (skip_present || !m_swap_chain)
|
if (skip_present || !m_swap_chain)
|
||||||
|
@ -668,6 +669,7 @@ bool VulkanGPUDevice::Render(bool skip_present)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void VulkanGPUDevice::BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height)
|
void VulkanGPUDevice::BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height)
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
|
|
||||||
void SetVSync(bool enabled) override;
|
void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
bool Render(bool skip_present) override;
|
//bool Render(bool skip_present) override;
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
|
@ -2645,3 +2645,36 @@ std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
|
||||||
|
|
||||||
return gpu;
|
return gpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<GPU> GPU::CreateHardwareOpenGLRenderer()
|
||||||
|
{
|
||||||
|
// Don't re-request GL when we already have GLES here...
|
||||||
|
const RenderAPI current_api = g_host_display ? g_host_display->GetRenderAPI() : RenderAPI::None;
|
||||||
|
if (current_api != RenderAPI::OpenGL && current_api != RenderAPI::OpenGLES &&
|
||||||
|
!Host::AcquireHostDisplay(RenderAPI::OpenGL))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Host render API type is incompatible");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
const bool opengl_is_available = ((g_host_display->GetRenderAPI() == RenderAPI::OpenGL &&
|
||||||
|
(GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
|
||||||
|
(g_host_display->GetRenderAPI() == RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_1));
|
||||||
|
if (!opengl_is_available)
|
||||||
|
{
|
||||||
|
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage",
|
||||||
|
"OpenGL renderer unavailable, your driver or hardware is not "
|
||||||
|
"recent enough. OpenGL 3.1 or OpenGL ES 3.1 is required."),
|
||||||
|
20.0f);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<GPU_HW> gpu(std::make_unique<GPU_HW>());
|
||||||
|
if (!gpu->Initialize())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return gpu;
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,107 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "gpu/gl/loader.h"
|
|
||||||
#include "gpu/gl/program.h"
|
|
||||||
#include "gpu/gl/shader_cache.h"
|
|
||||||
#include "gpu/gl/stream_buffer.h"
|
|
||||||
#include "gpu/gl/texture.h"
|
|
||||||
#include "gpu_hw.h"
|
|
||||||
#include "texture_replacements.h"
|
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
class GPU_HW_OpenGL final : public GPU_HW
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GPU_HW_OpenGL();
|
|
||||||
~GPU_HW_OpenGL() override;
|
|
||||||
|
|
||||||
bool Initialize() override;
|
|
||||||
void Reset(bool clear_vram) override;
|
|
||||||
|
|
||||||
void RestoreGraphicsAPIState() override;
|
|
||||||
void UpdateSettings() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void ClearDisplay() override;
|
|
||||||
void UpdateDisplay() override;
|
|
||||||
void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override;
|
|
||||||
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override;
|
|
||||||
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override;
|
|
||||||
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct GLStats
|
|
||||||
{
|
|
||||||
u32 num_batches;
|
|
||||||
u32 num_vertices;
|
|
||||||
u32 num_vram_reads;
|
|
||||||
u32 num_vram_writes;
|
|
||||||
u32 num_vram_read_texture_updates;
|
|
||||||
u32 num_uniform_buffer_updates;
|
|
||||||
};
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsGLES() const { return (m_render_api == RenderAPI::OpenGLES); }
|
|
||||||
|
|
||||||
void SetCapabilities();
|
|
||||||
bool CreateBuffers();
|
|
||||||
void ClearFramebuffer();
|
|
||||||
|
|
||||||
bool CreateVertexBuffer();
|
|
||||||
bool CreateUniformBuffer();
|
|
||||||
bool CreateTextureBuffer();
|
|
||||||
|
|
||||||
bool CompilePrograms();
|
|
||||||
|
|
||||||
void SetDepthFunc();
|
|
||||||
void SetDepthFunc(GLenum func);
|
|
||||||
void SetBlendMode();
|
|
||||||
|
|
||||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
|
||||||
void DownsampleFramebuffer(GL::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
|
||||||
void DownsampleFramebufferBoxFilter(GL::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
|
||||||
|
|
||||||
// downsample texture - used for readbacks at >1xIR.
|
|
||||||
GL::Texture m_vram_texture;
|
|
||||||
GL::Texture m_vram_depth_texture;
|
|
||||||
GL::Texture m_vram_read_texture;
|
|
||||||
GL::Texture m_vram_readback_texture;
|
|
||||||
GL::Texture m_display_texture;
|
|
||||||
GL::Texture m_vram_write_replacement_texture;
|
|
||||||
|
|
||||||
std::unique_ptr<GL::StreamBuffer> m_vertex_stream_buffer;
|
|
||||||
GLuint m_vram_fbo_id = 0;
|
|
||||||
GLuint m_vao_id = 0;
|
|
||||||
GLuint m_attributeless_vao_id = 0;
|
|
||||||
GLuint m_state_copy_fbo_id = 0;
|
|
||||||
|
|
||||||
std::unique_ptr<GL::StreamBuffer> m_uniform_stream_buffer;
|
|
||||||
|
|
||||||
std::unique_ptr<GL::StreamBuffer> m_texture_stream_buffer;
|
|
||||||
GLuint m_texture_buffer_r16ui_texture = 0;
|
|
||||||
|
|
||||||
std::array<std::array<std::array<std::array<GL::Program, 2>, 2>, 9>, 4>
|
|
||||||
m_render_programs; // [render_mode][texture_mode][dithering][interlacing]
|
|
||||||
std::array<std::array<GL::Program, 3>, 2> m_display_programs; // [depth_24][interlaced]
|
|
||||||
std::array<std::array<GL::Program, 2>, 2> m_vram_fill_programs;
|
|
||||||
GL::Program m_vram_read_program;
|
|
||||||
GL::Program m_vram_write_program;
|
|
||||||
GL::Program m_vram_copy_program;
|
|
||||||
GL::Program m_vram_update_depth_program;
|
|
||||||
|
|
||||||
u32 m_uniform_buffer_alignment = 1;
|
|
||||||
u32 m_texture_stream_buffer_size = 0;
|
|
||||||
|
|
||||||
bool m_use_texture_buffer_for_vram_writes = false;
|
|
||||||
bool m_use_ssbo_for_vram_writes = false;
|
|
||||||
|
|
||||||
GLenum m_current_depth_test = 0;
|
|
||||||
GPUTransparencyMode m_current_transparency_mode = GPUTransparencyMode::Disabled;
|
|
||||||
BatchRenderMode m_current_render_mode = BatchRenderMode::TransparencyDisabled;
|
|
||||||
|
|
||||||
GL::Texture m_downsample_texture;
|
|
||||||
GL::Program m_downsample_program;
|
|
||||||
};
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef WITH_OPENGL
|
#ifdef WITH_OPENGL
|
||||||
#include "gpu/gl/loader.h"
|
#include "gpu/opengl_loader.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Log_SetChannel(ShaderGen);
|
Log_SetChannel(ShaderGen);
|
||||||
|
@ -673,7 +673,7 @@ std::string ShaderGen::GenerateDisplayVertexShader()
|
||||||
float2 pos = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
float2 pos = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
||||||
v_tex0 = u_src_rect.xy + pos * u_src_rect.zw;
|
v_tex0 = u_src_rect.xy + pos * u_src_rect.zw;
|
||||||
v_pos = float4(pos * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
v_pos = float4(pos * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||||
#if API_OPENGL || API_OPENGL_ES || API_VULKAN
|
#if API_VULKAN
|
||||||
v_pos.y = -v_pos.y;
|
v_pos.y = -v_pos.y;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -711,7 +711,7 @@ std::string ShaderGen::GenerateImGuiVertexShader()
|
||||||
v_pos = mul(ProjectionMatrix, float4(a_pos, 0.f, 1.f));
|
v_pos = mul(ProjectionMatrix, float4(a_pos, 0.f, 1.f));
|
||||||
v_col0 = a_col0;
|
v_col0 = a_col0;
|
||||||
v_tex0 = a_tex0;
|
v_tex0 = a_tex0;
|
||||||
#if API_OPENGL || API_OPENGL_ES || API_VULKAN
|
#if API_VULKAN
|
||||||
v_pos.y = -v_pos.y;
|
v_pos.y = -v_pos.y;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_OPENGL
|
#ifdef WITH_OPENGL
|
||||||
#include "core/gpu/opengl_gpu_device.h"
|
#include "core/gpu/opengl_device.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_VULKAN
|
#ifdef WITH_VULKAN
|
||||||
|
@ -151,7 +151,7 @@ std::unique_ptr<GPUDevice> Host::CreateDisplayForAPI(RenderAPI api)
|
||||||
#ifdef WITH_OPENGL
|
#ifdef WITH_OPENGL
|
||||||
case RenderAPI::OpenGL:
|
case RenderAPI::OpenGL:
|
||||||
case RenderAPI::OpenGLES:
|
case RenderAPI::OpenGLES:
|
||||||
return std::make_unique<OpenGLGPUDevice>();
|
return std::make_unique<OpenGLDevice>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -168,7 +168,7 @@ std::unique_ptr<GPUDevice> Host::CreateDisplayForAPI(RenderAPI api)
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
return std::make_unique<D3D11Device>();
|
return std::make_unique<D3D11Device>();
|
||||||
#elif defined(WITH_OPENGL)
|
#elif defined(WITH_OPENGL)
|
||||||
return std::make_unique<OpenGLGPUDevice>();
|
return std::make_unique<OpenGLDevice>();
|
||||||
#elif defined(WITH_VULKAN)
|
#elif defined(WITH_VULKAN)
|
||||||
return std::make_unique<VulkanGPUDevice>();
|
return std::make_unique<VulkanGPUDevice>();
|
||||||
#else
|
#else
|
||||||
|
|
Loading…
Reference in New Issue