GL renderer
This commit is contained in:
parent
a43b6ee9db
commit
2792bf0239
|
@ -56,19 +56,17 @@
|
|||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<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">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\gpu_device.cpp" />
|
||||
<ClCompile Include="gpu\gpu_texture.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\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_shader.cpp" />
|
||||
<ClCompile Include="gpu\postprocessing_shadergen.cpp" />
|
||||
|
@ -97,9 +95,6 @@
|
|||
<ClCompile Include="gdb_protocol.cpp" />
|
||||
<ClCompile Include="gpu.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_interface_progress_callback.cpp" />
|
||||
<ClCompile Include="interrupt_controller.cpp" />
|
||||
|
@ -154,13 +149,13 @@
|
|||
<ClInclude Include="digital_controller.h" />
|
||||
<ClInclude Include="game_database.h" />
|
||||
<ClInclude Include="gpu\d3d11_device.h" />
|
||||
<ClInclude Include="gpu\d3d12\texture.h" />
|
||||
<ClInclude Include="gpu\d3d_shaders.h" />
|
||||
<ClInclude Include="gpu\d3d12\context.h" />
|
||||
<ClInclude Include="gpu\d3d12\descriptor_heap_manager.h" />
|
||||
<ClInclude Include="gpu\d3d12\shader_cache.h" />
|
||||
<ClInclude Include="gpu\d3d12\staging_texture.h" />
|
||||
<ClInclude Include="gpu\d3d12\stream_buffer.h" />
|
||||
<ClInclude Include="gpu\d3d12\texture.h" />
|
||||
<ClInclude Include="gpu\d3d12\util.h" />
|
||||
<ClInclude Include="gpu\d3d12_gpu_device.h" />
|
||||
<ClInclude Include="gpu\gl\context.h" />
|
||||
|
@ -180,20 +175,18 @@
|
|||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<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">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\gpu_device.h" />
|
||||
<ClInclude Include="gpu\gpu_texture.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\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_shader.h" />
|
||||
<ClInclude Include="gpu\postprocessing_shadergen.h" />
|
||||
|
@ -224,9 +217,6 @@
|
|||
<ClInclude Include="gdb_protocol.h" />
|
||||
<ClInclude Include="gpu.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="host.h" />
|
||||
<ClInclude Include="host_interface_progress_callback.h" />
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
<ClCompile Include="dma.cpp" />
|
||||
<ClCompile Include="gdb_protocol.cpp" />
|
||||
<ClCompile Include="gpu.cpp" />
|
||||
<ClCompile Include="gpu_hw_opengl.cpp" />
|
||||
<ClCompile Include="gpu_hw.cpp" />
|
||||
<ClCompile Include="interrupt_controller.cpp" />
|
||||
<ClCompile Include="cdrom.cpp" />
|
||||
|
@ -67,7 +66,7 @@
|
|||
<ClCompile Include="gpu\gpu_device.cpp">
|
||||
<Filter>gpu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\opengl_gpu_device.cpp">
|
||||
<ClCompile Include="gpu\opengl_device.cpp">
|
||||
<Filter>gpu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\vulkan_gpu_device.cpp">
|
||||
|
@ -82,9 +81,6 @@
|
|||
<ClCompile Include="gpu\d3d12\stream_buffer.cpp">
|
||||
<Filter>gpu\d3d12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\d3d12\texture.cpp">
|
||||
<Filter>gpu\d3d12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\d3d12\util.cpp">
|
||||
<Filter>gpu\d3d12</Filter>
|
||||
</ClCompile>
|
||||
|
@ -106,18 +102,6 @@
|
|||
<ClCompile Include="gpu\gl\context_wgl.cpp">
|
||||
<Filter>gpu\gl</Filter>
|
||||
</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">
|
||||
<Filter>gpu\gl</Filter>
|
||||
</ClCompile>
|
||||
|
@ -133,9 +117,6 @@
|
|||
<ClCompile Include="gpu\vulkan\swap_chain.cpp">
|
||||
<Filter>gpu\vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\vulkan\texture.cpp">
|
||||
<Filter>gpu\vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\vulkan\util.cpp">
|
||||
<Filter>gpu\vulkan</Filter>
|
||||
</ClCompile>
|
||||
|
@ -166,9 +147,6 @@
|
|||
<ClCompile Include="gpu\imgui_impl_dx12.cpp">
|
||||
<Filter>gpu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\imgui_impl_opengl3.cpp">
|
||||
<Filter>gpu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gpu\imgui_impl_vulkan.cpp">
|
||||
<Filter>gpu</Filter>
|
||||
</ClCompile>
|
||||
|
@ -178,6 +156,21 @@
|
|||
<ClCompile Include="gpu\gpu_shader_cache.cpp">
|
||||
<Filter>gpu</Filter>
|
||||
</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>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -189,7 +182,6 @@
|
|||
<ClInclude Include="bus.h" />
|
||||
<ClInclude Include="dma.h" />
|
||||
<ClInclude Include="gpu.h" />
|
||||
<ClInclude Include="gpu_hw_opengl.h" />
|
||||
<ClInclude Include="gpu_hw.h" />
|
||||
<ClInclude Include="interrupt_controller.h" />
|
||||
<ClInclude Include="cdrom.h" />
|
||||
|
@ -252,7 +244,7 @@
|
|||
<ClInclude Include="gpu\gpu_device.h">
|
||||
<Filter>gpu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\opengl_gpu_device.h">
|
||||
<ClInclude Include="gpu\opengl_device.h">
|
||||
<Filter>gpu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\vulkan_gpu_device.h">
|
||||
|
@ -267,9 +259,6 @@
|
|||
<ClInclude Include="gpu\d3d12\stream_buffer.h">
|
||||
<Filter>gpu\d3d12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\d3d12\texture.h">
|
||||
<Filter>gpu\d3d12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\d3d12\util.h">
|
||||
<Filter>gpu\d3d12</Filter>
|
||||
</ClInclude>
|
||||
|
@ -291,21 +280,6 @@
|
|||
<ClInclude Include="gpu\gl\context_wgl.h">
|
||||
<Filter>gpu\gl</Filter>
|
||||
</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">
|
||||
<Filter>gpu\gl</Filter>
|
||||
</ClInclude>
|
||||
|
@ -324,9 +298,6 @@
|
|||
<ClInclude Include="gpu\vulkan\swap_chain.h">
|
||||
<Filter>gpu\vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\vulkan\texture.h">
|
||||
<Filter>gpu\vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\vulkan\util.h">
|
||||
<Filter>gpu\vulkan</Filter>
|
||||
</ClInclude>
|
||||
|
@ -360,9 +331,6 @@
|
|||
<ClInclude Include="gpu\imgui_impl_dx12.h">
|
||||
<Filter>gpu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\imgui_impl_opengl3.h">
|
||||
<Filter>gpu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gpu\imgui_impl_vulkan.h">
|
||||
<Filter>gpu</Filter>
|
||||
</ClInclude>
|
||||
|
@ -375,6 +343,24 @@
|
|||
<ClInclude Include="gpu\gpu_shader_cache.h">
|
||||
<Filter>gpu</Filter>
|
||||
</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>
|
||||
<Filter Include="gpu">
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
virtual bool DoState(StateWrapper& sw, GPUTexture** save_to_texture, bool update_display);
|
||||
|
||||
// Graphics API state reset/restore - call when drawing the UI etc.
|
||||
// TODO: replace with "invalidate cached state"
|
||||
virtual void RestoreGraphicsAPIState();
|
||||
|
||||
// Render statistics debug window.
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <array>
|
||||
|
@ -36,6 +34,7 @@ static unsigned s_next_bad_shader_id = 1;
|
|||
|
||||
static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view& name)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// WKPDID_D3DDebugObjectName
|
||||
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);
|
||||
obj->SetPrivateData(guid, static_cast<UINT>(wname.length()) * 2u, wname.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
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)
|
||||
m_context.As(&m_annotation);
|
||||
#endif
|
||||
|
||||
// we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky.
|
||||
ComPtr<IDXGIDevice> dxgi_device;
|
||||
|
@ -600,22 +602,20 @@ bool D3D11Device::CreateSwapChainRTV()
|
|||
return false;
|
||||
}
|
||||
|
||||
m_swap_chain_texture = std::make_unique<D3D11Texture>();
|
||||
if (!m_swap_chain_texture->Adopt(m_device.Get(), std::move(backbuffer)))
|
||||
D3D11_TEXTURE2D_DESC backbuffer_desc;
|
||||
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;
|
||||
}
|
||||
|
||||
if (!(m_swap_chain_framebuffer = std::unique_ptr<D3D11Framebuffer>(
|
||||
static_cast<D3D11Framebuffer*>(CreateFramebuffer(m_swap_chain_texture.get()).release()))))
|
||||
{
|
||||
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();
|
||||
m_window_info.surface_width = backbuffer_desc.Width;
|
||||
m_window_info.surface_height = backbuffer_desc.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)
|
||||
|
@ -651,8 +651,7 @@ void D3D11Device::DestroySurface()
|
|||
if (IsFullscreen())
|
||||
SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
m_swap_chain_framebuffer.reset();
|
||||
m_swap_chain_texture.reset();
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain.Reset();
|
||||
}
|
||||
|
||||
|
@ -676,8 +675,7 @@ void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
|||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_swap_chain_framebuffer.reset();
|
||||
m_swap_chain_texture.reset();
|
||||
m_swap_chain_rtv.Reset();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||
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;
|
||||
}
|
||||
|
||||
m_swap_chain_framebuffer.reset();
|
||||
m_swap_chain_texture.reset();
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain.Reset();
|
||||
|
||||
if (!CreateSwapChain(&closest_mode))
|
||||
|
@ -782,13 +779,10 @@ void D3D11Device::DestroyBuffers()
|
|||
m_index_buffer.Release();
|
||||
}
|
||||
|
||||
bool D3D11Device::Render(bool skip_present)
|
||||
bool D3D11Device::BeginPresent(bool skip_present)
|
||||
{
|
||||
if (skip_present || !m_swap_chain)
|
||||
{
|
||||
ImGui::Render();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -797,23 +791,16 @@ bool D3D11Device::Render(bool skip_present)
|
|||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||
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())
|
||||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight());
|
||||
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();
|
||||
void D3D11Device::EndPresent()
|
||||
{
|
||||
DebugAssert(!m_current_framebuffer);
|
||||
|
||||
if (!m_vsync_enabled && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
@ -825,10 +812,6 @@ bool D3D11Device::Render(bool skip_present)
|
|||
|
||||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
|
||||
m_current_framebuffer = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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*/,
|
||||
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;
|
||||
}
|
||||
|
||||
const bool discard = (width == m_width && height == m_height);
|
||||
const u32 srnum = D3D11CalcSubresource(level, layer, m_levels);
|
||||
|
@ -1780,6 +1766,11 @@ void D3D11Texture::Unmap()
|
|||
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,
|
||||
Format format, const void* initial_data /* = nullptr */, u32 initial_data_stride /* = 0 */,
|
||||
bool dynamic /* = false */)
|
||||
|
@ -2016,6 +2007,7 @@ std::unique_ptr<GPUTextureBuffer> D3D11Device::CreateTextureBuffer(GPUTextureBuf
|
|||
|
||||
void D3D11Device::PushDebugGroup(const char* fmt, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!m_annotation)
|
||||
return;
|
||||
|
||||
|
@ -2025,18 +2017,22 @@ void D3D11Device::PushDebugGroup(const char* fmt, ...)
|
|||
va_end(ap);
|
||||
|
||||
m_annotation->BeginEvent(StringUtil::UTF8StringToWideString(str).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void D3D11Device::PopDebugGroup()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!m_annotation)
|
||||
return;
|
||||
|
||||
m_annotation->EndEvent();
|
||||
#endif
|
||||
}
|
||||
|
||||
void D3D11Device::InsertDebugMessage(const char* fmt, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!m_annotation)
|
||||
return;
|
||||
|
||||
|
@ -2046,6 +2042,7 @@ void D3D11Device::InsertDebugMessage(const char* fmt, ...)
|
|||
va_end(ap);
|
||||
|
||||
m_annotation->SetMarker(StringUtil::UTF8StringToWideString(str).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
void Unmap() override;
|
||||
|
||||
void SetDebugName(const std::string_view& name) override;
|
||||
|
||||
private:
|
||||
ComPtr<ID3D11Texture2D> m_texture;
|
||||
ComPtr<ID3D11ShaderResourceView> m_srv;
|
||||
|
@ -335,7 +337,8 @@ public:
|
|||
|
||||
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 UnbindPipeline(D3D11Pipeline* pl);
|
||||
|
@ -387,8 +390,7 @@ private:
|
|||
|
||||
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||
std::unique_ptr<D3D11Texture> m_swap_chain_texture;
|
||||
std::unique_ptr<D3D11Framebuffer> m_swap_chain_framebuffer;
|
||||
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||
|
||||
RasterizationStateMap m_rasterization_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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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:
|
||||
static bool CreateSRVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled,
|
||||
DescriptorHandle* dh);
|
||||
|
|
|
@ -573,6 +573,7 @@ bool D3D12GPUDevice::UpdateImGuiFontTexture()
|
|||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
bool D3D12GPUDevice::Render(bool skip_present)
|
||||
{
|
||||
if (skip_present || !m_swap_chain)
|
||||
|
@ -604,6 +605,7 @@ bool D3D12GPUDevice::Render(bool skip_present)
|
|||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool D3D12GPUDevice::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
bool Render(bool skip_present) override;
|
||||
//bool Render(bool skip_present) override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "context.h"
|
||||
#include "../opengl_loader.h"
|
||||
|
||||
#include "common/log.h"
|
||||
#include "loader.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#ifdef __APPLE__
|
||||
|
@ -11,7 +13,6 @@
|
|||
#else
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
Log_SetChannel(GL::Context);
|
||||
|
||||
#if defined(_WIN32) && !defined(_M_ARM64)
|
||||
#include "context_wgl.h"
|
||||
|
@ -32,6 +33,8 @@ Log_SetChannel(GL::Context);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
Log_SetChannel(GL::Context);
|
||||
|
||||
namespace GL {
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "context_wgl.h"
|
||||
#include "../opengl_loader.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/scoped_guard.h"
|
||||
#include "loader.h"
|
||||
|
||||
Log_SetChannel(GL::ContextWGL);
|
||||
|
||||
// TODO: get rid of this
|
||||
|
@ -33,7 +35,9 @@ static bool ReloadWGL(HDC dc)
|
|||
}
|
||||
|
||||
namespace GL {
|
||||
ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi) {}
|
||||
ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi)
|
||||
{
|
||||
}
|
||||
|
||||
ContextWGL::~ContextWGL()
|
||||
{
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
#include "common/windows_headers.h"
|
||||
#include "context.h"
|
||||
|
||||
#include "../opengl_loader.h"
|
||||
|
||||
#include "common/windows_headers.h"
|
||||
|
||||
#include "glad_wgl.h"
|
||||
#include "loader.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
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()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32___ // TODO remove me
|
||||
return RenderAPI::D3D11;
|
||||
#else
|
||||
return RenderAPI::OpenGL;
|
||||
|
@ -508,6 +508,19 @@ void GPUDevice::DrawIndexed(u32 base_index, u32 index_count, u32 base_vertex)
|
|||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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
|
||||
const u32 target_width = target ? target->GetWidth() : m_window_info.surface_width;
|
||||
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
|
||||
{
|
||||
SetFramebuffer(target);
|
||||
if (target)
|
||||
SetFramebuffer(target);
|
||||
else if (!BeginPresent(false))
|
||||
return false;
|
||||
}
|
||||
|
||||
SetPipeline(m_display_pipeline.get());
|
||||
|
@ -946,8 +1006,12 @@ void GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 wid
|
|||
|
||||
if (postfx)
|
||||
{
|
||||
m_post_processing_chain->Apply(target, left, top, width, height, texture_view_width, texture_view_height,
|
||||
target_width, target_height);
|
||||
return m_post_processing_chain->Apply(target, left, top, width, height, texture_view_width, texture_view_height,
|
||||
target_width, target_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,17 @@ public:
|
|||
BitField<u64, u8, 18, 4> max_lod;
|
||||
BitField<u64, u32, 32, 32> border_color;
|
||||
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();
|
||||
|
@ -98,7 +109,9 @@ enum class GPUShaderStage : u8
|
|||
{
|
||||
Vertex,
|
||||
Fragment,
|
||||
Compute
|
||||
Compute,
|
||||
|
||||
MaxCount
|
||||
};
|
||||
|
||||
class GPUShader
|
||||
|
@ -314,6 +327,17 @@ public:
|
|||
ALWAYS_INLINE bool operator<(const BlendState& rhs) const { return key < rhs.key; }
|
||||
// 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 GetAlphaBlendingState();
|
||||
};
|
||||
|
@ -398,6 +422,8 @@ public:
|
|||
std::vector<std::string> fullscreen_modes;
|
||||
};
|
||||
|
||||
static constexpr u32 MAX_TEXTURE_SAMPLERS = 4;
|
||||
|
||||
virtual ~GPUDevice();
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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.
|
||||
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 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,
|
||||
bool linear_filter);
|
||||
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
|
||||
{
|
||||
return (source_hash_low == key.source_hash_low && source_hash_high == key.source_hash_high &&
|
||||
entry_point_low == key.entry_point_low && entry_point_high == key.entry_point_high &&
|
||||
shader_type == key.shader_type && source_length == key.source_length);
|
||||
return (std::memcmp(this, &key, sizeof(*this)) == 0);
|
||||
}
|
||||
|
||||
bool GPUShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
|
||||
{
|
||||
return (source_hash_low != key.source_hash_low || source_hash_high != key.source_hash_high ||
|
||||
entry_point_low != key.entry_point_low || entry_point_high != key.entry_point_high ||
|
||||
shader_type != key.shader_type || source_length != key.source_length);
|
||||
return (std::memcmp(this, &key, sizeof(*this)) != 0);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -189,12 +193,8 @@ bool GPUShaderCache::ReadExisting(const std::string& index_filename, const std::
|
|||
return false;
|
||||
}
|
||||
|
||||
const CacheIndexKey key{static_cast<GPUShaderStage>(entry.shader_type),
|
||||
entry.source_length,
|
||||
entry.source_hash_low,
|
||||
entry.source_hash_high,
|
||||
entry.entry_point_low,
|
||||
entry.entry_point_high};
|
||||
const CacheIndexKey key{entry.shader_type, entry.source_length, 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};
|
||||
m_index.emplace(key, data);
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ GPUShaderCache::CacheIndexKey GPUShaderCache::GetCacheKey(GPUShaderStage stage,
|
|||
};
|
||||
|
||||
CacheIndexKey key = {};
|
||||
key.shader_type = stage;
|
||||
key.shader_type = static_cast<u32>(stage);
|
||||
|
||||
MD5Digest digest;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ public:
|
|||
|
||||
struct CacheIndexKey
|
||||
{
|
||||
GPUShaderStage shader_type;
|
||||
u32 shader_type;
|
||||
u32 source_length;
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
|
@ -29,6 +29,12 @@ public:
|
|||
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();
|
||||
|
@ -46,24 +52,13 @@ public:
|
|||
void Clear();
|
||||
|
||||
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
|
||||
{
|
||||
u32 file_offset;
|
||||
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 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)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class GPUTexture
|
||||
|
@ -82,6 +85,7 @@ public:
|
|||
ALWAYS_INLINE bool IsDepthStencil() const { return (m_type == Type::DepthStencil); }
|
||||
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 float GetClearDepth() const { return m_clear_value.depth; }
|
||||
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.
|
||||
virtual void MakeReadyForSampling();
|
||||
|
||||
virtual void SetDebugName(const std::string_view& name) = 0;
|
||||
|
||||
protected:
|
||||
GPUTexture();
|
||||
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)
|
||||
|
||||
#include "stream_buffer.h"
|
||||
#include "opengl_stream_buffer.h"
|
||||
|
||||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
|
||||
namespace GL {
|
||||
|
||||
StreamBuffer::StreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
OpenGLStreamBuffer::OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: m_target(target), m_buffer_id(buffer_id), m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
StreamBuffer::~StreamBuffer()
|
||||
OpenGLStreamBuffer::~OpenGLStreamBuffer()
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer_id);
|
||||
}
|
||||
|
||||
void StreamBuffer::Bind()
|
||||
void OpenGLStreamBuffer::Bind()
|
||||
{
|
||||
glBindBuffer(m_target, m_buffer_id);
|
||||
}
|
||||
|
||||
void StreamBuffer::Unbind()
|
||||
void OpenGLStreamBuffer::Unbind()
|
||||
{
|
||||
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.
|
||||
class BufferSubDataStreamBuffer final : public StreamBuffer
|
||||
class BufferSubDataStreamBuffer final : public OpenGLStreamBuffer
|
||||
{
|
||||
public:
|
||||
~BufferSubDataStreamBuffer() override = default;
|
||||
~BufferSubDataStreamBuffer() override { _aligned_free(m_cpu_buffer); }
|
||||
|
||||
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)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
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();
|
||||
|
||||
|
@ -63,43 +77,49 @@ public:
|
|||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
glBindBuffer(target, 0);
|
||||
glDeleteBuffers(1, &buffer_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::unique_ptr<StreamBuffer>(new BufferSubDataStreamBuffer(target, buffer_id, size));
|
||||
return std::unique_ptr<OpenGLStreamBuffer>(new BufferSubDataStreamBuffer(target, buffer_id, size));
|
||||
}
|
||||
|
||||
private:
|
||||
BufferSubDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: StreamBuffer(target, buffer_id, size), m_cpu_buffer(size)
|
||||
BufferSubDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size) : OpenGLStreamBuffer(target, buffer_id, 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.
|
||||
class BufferDataStreamBuffer final : public StreamBuffer
|
||||
class BufferDataStreamBuffer final : public OpenGLStreamBuffer
|
||||
{
|
||||
public:
|
||||
~BufferDataStreamBuffer() override = default;
|
||||
~BufferDataStreamBuffer() override { _aligned_free(m_cpu_buffer); }
|
||||
|
||||
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)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
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();
|
||||
|
||||
|
@ -111,24 +131,27 @@ public:
|
|||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
glBindBuffer(target, 0);
|
||||
glDeleteBuffers(1, &buffer_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::unique_ptr<StreamBuffer>(new BufferDataStreamBuffer(target, buffer_id, size));
|
||||
return std::unique_ptr<OpenGLStreamBuffer>(new BufferDataStreamBuffer(target, buffer_id, size));
|
||||
}
|
||||
|
||||
private:
|
||||
BufferDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: StreamBuffer(target, buffer_id, size), m_cpu_buffer(size)
|
||||
BufferDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size) : OpenGLStreamBuffer(target, buffer_id, 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.
|
||||
class SyncingStreamBuffer : public StreamBuffer
|
||||
class SyncingStreamBuffer : public OpenGLStreamBuffer
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
|
@ -147,13 +170,13 @@ public:
|
|||
|
||||
protected:
|
||||
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);
|
||||
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);
|
||||
glDeleteSync(sync);
|
||||
sync = nullptr;
|
||||
}
|
||||
|
||||
void EnsureSyncsWaitedForOffset(u32 offset)
|
||||
ALWAYS_INLINE void EnsureSyncsWaitedForOffset(u32 offset)
|
||||
{
|
||||
const u32 end = std::min<u32>(GetSyncIndexForOffset(offset) + 1, NUM_SYNC_POINTS);
|
||||
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_used_block_index = 0;
|
||||
u32 m_available_block_index = NUM_SYNC_POINTS;
|
||||
|
@ -221,6 +246,7 @@ public:
|
|||
{
|
||||
glBindBuffer(m_target, m_buffer_id);
|
||||
glUnmapBuffer(m_target);
|
||||
glBindBuffer(m_target, 0);
|
||||
}
|
||||
|
||||
MappingResult Map(u32 alignment, u32 min_size) override
|
||||
|
@ -236,19 +262,22 @@ public:
|
|||
free_space_in_block / alignment};
|
||||
}
|
||||
|
||||
void Unmap(u32 used_size) override
|
||||
u32 Unmap(u32 used_size) override
|
||||
{
|
||||
DebugAssert((m_position + used_size) <= m_size);
|
||||
if (!m_coherent)
|
||||
{
|
||||
// TODO: shouldn't be needed anymore
|
||||
Bind();
|
||||
glFlushMappedBufferRange(m_target, m_position, used_size);
|
||||
}
|
||||
|
||||
const u32 prev_position = m_position;
|
||||
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();
|
||||
|
||||
|
@ -266,14 +295,16 @@ public:
|
|||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
glBindBuffer(target, 0);
|
||||
glDeleteBuffers(1, &buffer_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
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:
|
||||
|
@ -286,14 +317,14 @@ private:
|
|||
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)
|
||||
{
|
||||
buf = detail::BufferStorageStreamBuffer::Create(target, size);
|
||||
buf = BufferStorageStreamBuffer::Create(target, size);
|
||||
if (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)
|
||||
{
|
||||
// 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
|
||||
return detail::BufferDataStreamBuffer::Create(target, size);
|
||||
return BufferDataStreamBuffer::Create(target, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace GL
|
|
@ -2,17 +2,20 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "opengl_loader.h"
|
||||
|
||||
#include "common/types.h"
|
||||
#include "loader.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace GL {
|
||||
class StreamBuffer
|
||||
class OpenGLStreamBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~StreamBuffer();
|
||||
virtual ~OpenGLStreamBuffer();
|
||||
|
||||
ALWAYS_INLINE GLuint GetGLBufferId() const { return m_buffer_id; }
|
||||
ALWAYS_INLINE GLenum GetGLTarget() const { return m_target; }
|
||||
|
@ -21,6 +24,8 @@ public:
|
|||
void Bind();
|
||||
void Unbind();
|
||||
|
||||
void SetDebugName(const std::string_view& name);
|
||||
|
||||
struct MappingResult
|
||||
{
|
||||
void* pointer;
|
||||
|
@ -30,15 +35,19 @@ public:
|
|||
};
|
||||
|
||||
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:
|
||||
StreamBuffer(GLenum target, GLuint buffer_id, u32 size);
|
||||
OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size);
|
||||
|
||||
GLenum m_target;
|
||||
GLuint m_buffer_id;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
input->MakeReadyForSampling();
|
||||
|
||||
bool result = true;
|
||||
const PostProcessingShader& final_stage = m_shaders.back();
|
||||
for (PostProcessingShader& stage : m_shaders)
|
||||
{
|
||||
|
@ -267,7 +268,18 @@ void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s3
|
|||
if (!is_final)
|
||||
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->SetTextureSampler(0, input, m_border_sampler.get());
|
||||
|
@ -288,4 +300,5 @@ void PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s3
|
|||
}
|
||||
|
||||
GL_POP();
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
|
|
@ -71,6 +71,16 @@ bool Vulkan::Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* da
|
|||
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*/,
|
||||
u32 level /*= 0*/)
|
||||
{
|
||||
|
|
|
@ -74,6 +74,9 @@ public:
|
|||
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);
|
||||
|
||||
void SetDebugName(const std::string_view& name) override;
|
||||
void MakeReadyForSampling() override;
|
||||
|
||||
private:
|
||||
VkImageViewType m_view_type = VK_IMAGE_VIEW_TYPE_2D;
|
||||
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
|
|
@ -593,6 +593,7 @@ bool VulkanGPUDevice::DoneCurrent()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool VulkanGPUDevice::Render(bool skip_present)
|
||||
{
|
||||
if (skip_present || !m_swap_chain)
|
||||
|
@ -668,6 +669,7 @@ bool VulkanGPUDevice::Render(bool skip_present)
|
|||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void VulkanGPUDevice::BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height)
|
||||
{
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
bool Render(bool skip_present) override;
|
||||
//bool Render(bool skip_present) override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
|
|
@ -2645,3 +2645,36 @@ std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
|
|||
|
||||
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>
|
||||
|
||||
#ifdef WITH_OPENGL
|
||||
#include "gpu/gl/loader.h"
|
||||
#include "gpu/opengl_loader.h"
|
||||
#endif
|
||||
|
||||
Log_SetChannel(ShaderGen);
|
||||
|
@ -673,7 +673,7 @@ std::string ShaderGen::GenerateDisplayVertexShader()
|
|||
float2 pos = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
||||
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);
|
||||
#if API_OPENGL || API_OPENGL_ES || API_VULKAN
|
||||
#if API_VULKAN
|
||||
v_pos.y = -v_pos.y;
|
||||
#endif
|
||||
}
|
||||
|
@ -711,7 +711,7 @@ std::string ShaderGen::GenerateImGuiVertexShader()
|
|||
v_pos = mul(ProjectionMatrix, float4(a_pos, 0.f, 1.f));
|
||||
v_col0 = a_col0;
|
||||
v_tex0 = a_tex0;
|
||||
#if API_OPENGL || API_OPENGL_ES || API_VULKAN
|
||||
#if API_VULKAN
|
||||
v_pos.y = -v_pos.y;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
#endif
|
||||
|
||||
#ifdef WITH_OPENGL
|
||||
#include "core/gpu/opengl_gpu_device.h"
|
||||
#include "core/gpu/opengl_device.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN
|
||||
|
@ -151,7 +151,7 @@ std::unique_ptr<GPUDevice> Host::CreateDisplayForAPI(RenderAPI api)
|
|||
#ifdef WITH_OPENGL
|
||||
case RenderAPI::OpenGL:
|
||||
case RenderAPI::OpenGLES:
|
||||
return std::make_unique<OpenGLGPUDevice>();
|
||||
return std::make_unique<OpenGLDevice>();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -168,7 +168,7 @@ std::unique_ptr<GPUDevice> Host::CreateDisplayForAPI(RenderAPI api)
|
|||
#elif defined(_WIN32)
|
||||
return std::make_unique<D3D11Device>();
|
||||
#elif defined(WITH_OPENGL)
|
||||
return std::make_unique<OpenGLGPUDevice>();
|
||||
return std::make_unique<OpenGLDevice>();
|
||||
#elif defined(WITH_VULKAN)
|
||||
return std::make_unique<VulkanGPUDevice>();
|
||||
#else
|
||||
|
|
Loading…
Reference in New Issue