Merge pull request #1439 from Armada651/ogl-stereo-3d

OGL: Stereoscopic 3D Support
This commit is contained in:
Ryan Houdek
2014-11-28 11:45:38 -06:00
48 changed files with 774 additions and 267 deletions

View File

@ -32,3 +32,5 @@ $99 of some treasures
040AE530 3F000063 040AE530 3F000063
$End Boss Has No HP $End Boss Has No HP
[Video_Stereoscopy]
StereoMonoEFBDepth = True

View File

@ -1,24 +0,0 @@
// Omega's 3D Stereoscopic filtering
// TODO: Need depth info!
void main()
{
// Source Color
float4 c0 = Sample();
const int sep = 5;
float red = c0.r;
float green = c0.g;
float blue = c0.b;
// Left Eye (Red)
float4 c1 = SampleOffset(int2(sep, 0));
red = max(c0.r, c1.r);
// Right Eye (Cyan)
float4 c2 = SampleOffset(int2(-sep, 0));
float cyan = (c2.g + c2.b) / 2.0;
green = max(c0.g, cyan);
blue = max(c0.b, cyan);
SetOutput(float4(red, green, blue, c0.a));
}

View File

@ -1,24 +0,0 @@
// Omega's 3D Stereoscopic filtering (Amber/Blue)
// TODO: Need depth info!
void main()
{
// Source Color
float4 c0 = Sample();
float sep = 5.0;
float red = c0.r;
float green = c0.g;
float blue = c0.b;
// Left Eye (Amber)
float4 c2 = SampleLocation(GetCoordinates() + float2(sep,0.0)*GetInvResolution()).rgba;
float amber = (c2.r + c2.g) / 2.0;
red = max(c0.r, amber);
green = max(c0.g, amber);
// Right Eye (Blue)
float4 c1 = SampleLocation(GetCoordinates() + float2(-sep,0.0)*GetInvResolution()).rgba;
blue = max(c0.b, c1.b);
SetOutput(float4(red, green, blue, c0.a));
}

View File

@ -351,6 +351,13 @@ void Matrix44::Translate(Matrix44 &mtx, const float vec[3])
mtx.data[11] = vec[2]; mtx.data[11] = vec[2];
} }
void Matrix44::Shear(Matrix44 &mtx, const float a, const float b)
{
LoadIdentity(mtx);
mtx.data[2] = a;
mtx.data[6] = b;
}
void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result) void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result)
{ {
MatrixMul(4, a.data, b.data, result.data); MatrixMul(4, a.data, b.data, result.data);

View File

@ -232,6 +232,7 @@ public:
static void Set(Matrix44 &mtx, const float mtxArray[16]); static void Set(Matrix44 &mtx, const float mtxArray[16]);
static void Translate(Matrix44 &mtx, const float vec[3]); static void Translate(Matrix44 &mtx, const float vec[3]);
static void Shear(Matrix44 &mtx, const float a, const float b = 0);
static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result); static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result);

View File

@ -81,6 +81,11 @@ static const struct
{ "FreelookZoomOut", 83 /* 'S' */, 4 /* wxMOD_SHIFT */ }, { "FreelookZoomOut", 83 /* 'S' */, 4 /* wxMOD_SHIFT */ },
{ "FreelookReset", 82 /* 'R' */, 4 /* wxMOD_SHIFT */ }, { "FreelookReset", 82 /* 'R' */, 4 /* wxMOD_SHIFT */ },
{ "IncreaseSeparation", 0, 0 /* wxMOD_NONE */ },
{ "DecreaseSeparation", 0, 0 /* wxMOD_NONE */ },
{ "IncreaseConvergence", 0, 0 /* wxMOD_NONE */ },
{ "DecreaseConvergence", 0, 0 /* wxMOD_NONE */ },
{ "LoadStateSlot1", 340 /* WXK_F1 */, 0 /* wxMOD_NONE */ }, { "LoadStateSlot1", 340 /* WXK_F1 */, 0 /* wxMOD_NONE */ },
{ "LoadStateSlot2", 341 /* WXK_F2 */, 0 /* wxMOD_NONE */ }, { "LoadStateSlot2", 341 /* WXK_F2 */, 0 /* wxMOD_NONE */ },
{ "LoadStateSlot3", 342 /* WXK_F3 */, 0 /* wxMOD_NONE */ }, { "LoadStateSlot3", 342 /* WXK_F3 */, 0 /* wxMOD_NONE */ },

View File

@ -54,6 +54,11 @@ enum Hotkey
HK_FREELOOK_ZOOM_OUT, HK_FREELOOK_ZOOM_OUT,
HK_FREELOOK_RESET, HK_FREELOOK_RESET,
HK_INCREASE_SEPARATION,
HK_DECREASE_SEPARATION,
HK_INCREASE_CONVERGENCE,
HK_DECREASE_CONVERGENCE,
HK_LOAD_STATE_SLOT_1, HK_LOAD_STATE_SLOT_1,
HK_LOAD_STATE_SLOT_2, HK_LOAD_STATE_SLOT_2,
HK_LOAD_STATE_SLOT_3, HK_LOAD_STATE_SLOT_3,

View File

@ -1123,6 +1123,26 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
{ {
State::Load(g_saveSlot); State::Load(g_saveSlot);
} }
else if (IsHotkey(event, HK_INCREASE_SEPARATION))
{
if (++g_Config.iStereoSeparation > 100)
g_Config.iStereoSeparation = 100;
}
else if (IsHotkey(event, HK_DECREASE_SEPARATION))
{
if (--g_Config.iStereoSeparation < 0)
g_Config.iStereoSeparation = 0;
}
else if (IsHotkey(event, HK_INCREASE_CONVERGENCE))
{
if (++g_Config.iStereoConvergence > 500)
g_Config.iStereoConvergence = 500;
}
else if (IsHotkey(event, HK_DECREASE_CONVERGENCE))
{
if (--g_Config.iStereoConvergence < 0)
g_Config.iStereoConvergence = 0;
}
else else
{ {

View File

@ -238,6 +238,11 @@ void HotkeyConfigDialog::CreateHotkeyGUIControls()
_("Freelook Zoom Out"), _("Freelook Zoom Out"),
_("Freelook Reset"), _("Freelook Reset"),
_("Increase Stereocopy Separation"),
_("Decrease Stereocopy Separation"),
_("Increase Stereocopy Convergence"),
_("Decrease Stereocopy Convergence"),
_("Load State Slot 1"), _("Load State Slot 1"),
_("Load State Slot 2"), _("Load State Slot 2"),
_("Load State Slot 3"), _("Load State Slot 3"),

View File

@ -149,6 +149,10 @@ static wxString crop_desc = wxTRANSLATE("Crop the picture from 4:3 to 5:4 or fro
static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect after finishing a frame.\n\nIf unsure, select (off)."); static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect after finishing a frame.\n\nIf unsure, select (off).");
static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked."); static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked."); static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D mode, stereoscopy allows you to get a better feeling of depth if you have the necessary hardware.\nSide-by-Side and Top-and-Bottom are used by most 3D TVs.\nAnaglyph is used for Red-Cyan colored glasses.\nHeavily decreases emulation speed and sometimes causes issues.\n\nIf unsure, select Off.");
static wxString stereo_separation_desc = wxTRANSLATE("Control the separation distance, this is the distance between the virtual cameras.\nA higher value creates a stronger feeling of depth while a lower value is more comfortable.");
static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA higher value creates stronger out-of-screen effects while a lower value is more comfortable.");
static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye, mostly useful if you want to view side-by-side cross-eyed.\n\nIf unsure, leave this unchecked.");
#if !defined(__APPLE__) #if !defined(__APPLE__)
@ -396,7 +400,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
if (vconfig.backend_info.PPShaders.size()) if (vconfig.backend_info.PPShaders.size())
{ {
wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5); wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5);
wxChoice *const choice_ppshader = new wxChoice(page_enh, -1); choice_ppshader = new wxChoice(page_enh, -1);
RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc)); RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc));
choice_ppshader->AppendString(_("(off)")); choice_ppshader->AppendString(_("(off)"));
@ -425,6 +429,11 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
szr_pp->Add(button_config_pp); szr_pp->Add(button_config_pp);
szr_enh->Add(szr_pp); szr_enh->Add(szr_pp);
} }
else
{
choice_ppshader = nullptr;
button_config_pp = nullptr;
}
// Scaled copy, PL, Bilinear filter // Scaled copy, PL, Bilinear filter
szr_enh->Add(CreateCheckBox(page_enh, _("Scaled EFB Copy"), wxGetTranslation(scaled_efb_copy_desc), vconfig.bCopyEFBScaled)); szr_enh->Add(CreateCheckBox(page_enh, _("Scaled EFB Copy"), wxGetTranslation(scaled_efb_copy_desc), vconfig.bCopyEFBScaled));
@ -438,6 +447,36 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
szr_enh_main->Add(group_enh, 0, wxEXPAND | wxALL, 5); szr_enh_main->Add(group_enh, 0, wxEXPAND | wxALL, 5);
// - stereoscopy
if (vconfig.backend_info.bSupportsStereoscopy && vconfig.iStereoMode > 0)
{
wxFlexGridSizer* const szr_stereo = new wxFlexGridSizer(2, 5, 5);
const wxString stereo_choices[] = { "Off", "Side-by-Side", "Top-and-Bottom", "Anaglyph" };
szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereoscopic 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices));
wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 0, 100, wxDefaultPosition, wxDefaultSize);
sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Separation:")), 1, wxALIGN_CENTER_VERTICAL, 0);
szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT);
wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoConvergence, 0, 500, wxDefaultPosition, wxDefaultSize);
conv_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
RegisterControl(conv_slider, wxGetTranslation(stereo_convergence_desc));
szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Convergence:")), 1, wxALIGN_CENTER_VERTICAL, 0);
szr_stereo->Add(conv_slider, 0, wxEXPAND | wxRIGHT);
szr_stereo->Add(CreateCheckBox(page_enh, _("Swap Eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
wxStaticBoxSizer* const group_stereo = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Stereoscopy"));
group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
szr_enh_main->Add(group_stereo, 0, wxEXPAND | wxALL, 5);
}
szr_enh_main->AddStretchSpacer(); szr_enh_main->AddStretchSpacer();
CreateDescriptionArea(page_enh, szr_enh_main); CreateDescriptionArea(page_enh, szr_enh_main);

View File

@ -165,6 +165,20 @@ protected:
ev.Skip(); ev.Skip();
} }
void Event_StereoSep(wxCommandEvent &ev)
{
vconfig.iStereoSeparation = ev.GetInt();
ev.Skip();
}
void Event_StereoFoc(wxCommandEvent &ev)
{
vconfig.iStereoConvergence = ev.GetInt();
ev.Skip();
}
void Event_ClickClose(wxCommandEvent&); void Event_ClickClose(wxCommandEvent&);
void Event_Close(wxCloseEvent&); void Event_Close(wxCloseEvent&);
@ -184,6 +198,12 @@ protected:
virtual_xfb->Enable(vconfig.bUseXFB); virtual_xfb->Enable(vconfig.bUseXFB);
real_xfb->Enable(vconfig.bUseXFB); real_xfb->Enable(vconfig.bUseXFB);
// PP Shaders
if (choice_ppshader)
choice_ppshader->Enable(vconfig.iStereoMode != STEREO_ANAGLYPH);
if (button_config_pp)
button_config_pp->Enable(vconfig.iStereoMode != STEREO_ANAGLYPH);
// Things which shouldn't be changed during emulation // Things which shouldn't be changed during emulation
if (Core::IsRunning()) if (Core::IsRunning())
{ {
@ -248,6 +268,8 @@ protected:
wxCheckBox* progressive_scan_checkbox; wxCheckBox* progressive_scan_checkbox;
wxChoice* choice_ppshader;
std::map<wxWindow*, wxString> ctrl_descs; // maps setting controls to their descriptions std::map<wxWindow*, wxString> ctrl_descs; // maps setting controls to their descriptions
std::map<wxWindow*, wxStaticText*> desc_texts; // maps dialog tabs (which are the parents of the setting controls) to their description text objects std::map<wxWindow*, wxStaticText*> desc_texts; // maps dialog tabs (which are the parents of the setting controls) to their description text objects

View File

@ -172,7 +172,7 @@ bool LineGeometryShader::SetShader(u32 components, float lineWidth,
static char buffer[16384]; static char buffer[16384];
ShaderCode code; ShaderCode code;
code.SetBuffer(buffer); code.SetBuffer(buffer);
GenerateVSOutputStructForGS(code, API_D3D); GenerateVSOutputStruct(code, API_D3D);
code.Write("\n%s", LINE_GS_COMMON); code.Write("\n%s", LINE_GS_COMMON);
std::stringstream numTexCoordsStream; std::stringstream numTexCoordsStream;

View File

@ -166,7 +166,7 @@ bool PointGeometryShader::SetShader(u32 components, float pointSize,
static char buffer[16384]; static char buffer[16384];
ShaderCode code; ShaderCode code;
code.SetBuffer(buffer); code.SetBuffer(buffer);
GenerateVSOutputStructForGS(code, API_D3D); GenerateVSOutputStruct(code, API_D3D);
code.Write("\n%s", POINT_GS_COMMON); code.Write("\n%s", POINT_GS_COMMON);
std::stringstream numTexCoordsStream; std::stringstream numTexCoordsStream;

View File

@ -43,6 +43,9 @@ private:
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) override; TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) override;
u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) {return 0;}; u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) {return 0;};
void CompileShaders() override { }
void DeleteShaders() override { }
}; };
} }

View File

@ -78,6 +78,7 @@ void InitBackendInfo()
g_Config.backend_info.bSupportsPrimitiveRestart = true; g_Config.backend_info.bSupportsPrimitiveRestart = true;
g_Config.backend_info.bSupportsOversizedViewports = false; g_Config.backend_info.bSupportsOversizedViewports = false;
g_Config.backend_info.bSupportsBBox = false; // TODO: not implemented g_Config.backend_info.bSupportsBBox = false; // TODO: not implemented
g_Config.backend_info.bSupportsStereoscopy = false; // TODO: not implemented
IDXGIFactory* factory; IDXGIFactory* factory;
IDXGIAdapter* ad; IDXGIAdapter* ad;

View File

@ -21,7 +21,6 @@ int FramebufferManager::m_targetHeight;
int FramebufferManager::m_msaaSamples; int FramebufferManager::m_msaaSamples;
GLenum FramebufferManager::m_textureType; GLenum FramebufferManager::m_textureType;
GLuint FramebufferManager::m_efbFramebuffer; GLuint FramebufferManager::m_efbFramebuffer;
GLuint FramebufferManager::m_xfbFramebuffer; GLuint FramebufferManager::m_xfbFramebuffer;
GLuint FramebufferManager::m_efbColor; GLuint FramebufferManager::m_efbColor;
@ -72,42 +71,45 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
m_efbDepth = glObj[1]; m_efbDepth = glObj[1];
m_efbColorSwap = glObj[2]; m_efbColorSwap = glObj[2];
m_EFBLayers = (g_ActiveConfig.iStereoMode > 0) ? 2 : 1;
// OpenGL MSAA textures are a different kind of texture type and must be allocated // OpenGL MSAA textures are a different kind of texture type and must be allocated
// with a different function, so we create them separately. // with a different function, so we create them separately.
if (m_msaaSamples <= 1) if (m_msaaSamples <= 1)
{ {
m_textureType = GL_TEXTURE_2D; m_textureType = GL_TEXTURE_2D_ARRAY;
glBindTexture(m_textureType, m_efbColor); glBindTexture(m_textureType, m_efbColor);
glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(m_textureType, m_efbDepth); glBindTexture(m_textureType, m_efbDepth);
glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(m_textureType, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); glTexImage3D(m_textureType, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
glBindTexture(m_textureType, m_efbColorSwap); glBindTexture(m_textureType, m_efbColorSwap);
glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
} }
else else
{ {
m_textureType = GL_TEXTURE_2D_MULTISAMPLE; m_textureType = GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
GLenum resolvedType = GL_TEXTURE_2D_ARRAY;
glBindTexture(m_textureType, m_efbColor); glBindTexture(m_textureType, m_efbColor);
glTexImage2DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, false); glTexImage3DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, false);
glBindTexture(m_textureType, m_efbDepth); glBindTexture(m_textureType, m_efbDepth);
glTexImage2DMultisample(m_textureType, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, false); glTexImage3DMultisample(m_textureType, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, m_EFBLayers, false);
glBindTexture(m_textureType, m_efbColorSwap); glBindTexture(m_textureType, m_efbColorSwap);
glTexImage2DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, false); glTexImage3DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, false);
glBindTexture(m_textureType, 0); glBindTexture(m_textureType, 0);
// Although we are able to access the multisampled texture directly, we don't do it everywhere. // Although we are able to access the multisampled texture directly, we don't do it everywhere.
@ -118,23 +120,23 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
m_resolvedColorTexture = glObj[0]; m_resolvedColorTexture = glObj[0];
m_resolvedDepthTexture = glObj[1]; m_resolvedDepthTexture = glObj[1];
glBindTexture(GL_TEXTURE_2D, m_resolvedColorTexture); glBindTexture(resolvedType, m_resolvedColorTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(resolvedType, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(resolvedType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(resolvedType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(resolvedType, 0, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, m_resolvedDepthTexture); glBindTexture(resolvedType, m_resolvedDepthTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(resolvedType, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(resolvedType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(resolvedType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); glTexImage3D(resolvedType, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
// Bind resolved textures to resolved framebuffer. // Bind resolved textures to resolved framebuffer.
glGenFramebuffers(1, &m_resolvedFramebuffer); glGenFramebuffers(1, &m_resolvedFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_resolvedFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_resolvedFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_resolvedColorTexture, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_resolvedColorTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_resolvedDepthTexture, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_resolvedDepthTexture, 0);
} }
// Create XFB framebuffer; targets will be created elsewhere. // Create XFB framebuffer; targets will be created elsewhere.
@ -143,8 +145,8 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
// Bind target textures to EFB framebuffer. // Bind target textures to EFB framebuffer.
glGenFramebuffers(1, &m_efbFramebuffer); glGenFramebuffers(1, &m_efbFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textureType, m_efbColor, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_efbColor, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_textureType, m_efbDepth, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_efbDepth, 0);
// EFB framebuffer is currently bound, make sure to clear its alpha value to 1.f // EFB framebuffer is currently bound, make sure to clear its alpha value to 1.f
glViewport(0, 0, m_targetWidth, m_targetHeight); glViewport(0, 0, m_targetWidth, m_targetHeight);
@ -168,9 +170,9 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
{ {
// non-msaa, so just fetch the pixel // non-msaa, so just fetch the pixel
sampler = sampler =
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"vec4 sampleEFB(ivec2 pos) {\n" "vec4 sampleEFB(ivec2 pos) {\n"
" return texelFetch(samp9, pos, 0);\n" " return texelFetch(samp9, ivec3(pos, 0), 0);\n"
"}\n"; "}\n";
} }
else if (g_ogl_config.bSupportSampleShading) else if (g_ogl_config.bSupportSampleShading)
@ -179,9 +181,9 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
// This will lead to sample shading, but it's the only way to not loose // This will lead to sample shading, but it's the only way to not loose
// the values of each sample. // the values of each sample.
sampler = sampler =
"SAMPLER_BINDING(9) uniform sampler2DMS samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DMSArray samp9;\n"
"vec4 sampleEFB(ivec2 pos) {\n" "vec4 sampleEFB(ivec2 pos) {\n"
" return texelFetch(samp9, pos, gl_SampleID);\n" " return texelFetch(samp9, ivec3(pos, 0), gl_SampleID);\n"
"}\n"; "}\n";
} }
else else
@ -190,11 +192,11 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
std::stringstream samples; std::stringstream samples;
samples << m_msaaSamples; samples << m_msaaSamples;
sampler = sampler =
"SAMPLER_BINDING(9) uniform sampler2DMS samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DMSArray samp9;\n"
"vec4 sampleEFB(ivec2 pos) {\n" "vec4 sampleEFB(ivec2 pos) {\n"
" vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n" " vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n"
" for(int i=0; i<" + samples.str() + "; i++)\n" " for(int i=0; i<" + samples.str() + "; i++)\n"
" color += texelFetch(samp9, pos, i);\n" " color += texelFetch(samp9, ivec3(pos, 0), i);\n"
" return color / " + samples.str() + ";\n" " return color / " + samples.str() + ";\n"
"}\n"; "}\n";
} }
@ -365,7 +367,7 @@ void FramebufferManager::ReinterpretPixelData(unsigned int convtype)
src_texture = m_efbColor; src_texture = m_efbColor;
m_efbColor = m_efbColorSwap; m_efbColor = m_efbColorSwap;
m_efbColorSwap = src_texture; m_efbColorSwap = src_texture;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textureType, m_efbColor, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_efbColor, 0);
glViewport(0,0, m_targetWidth, m_targetHeight); glViewport(0,0, m_targetWidth, m_targetHeight);
glActiveTexture(GL_TEXTURE0 + 9); glActiveTexture(GL_TEXTURE0 + 9);
@ -397,7 +399,7 @@ void XFBSource::CopyEFB(float Gamma)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferManager::GetXFBFramebuffer()); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferManager::GetXFBFramebuffer());
// Bind texture. // Bind texture.
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
glBlitFramebuffer( glBlitFramebuffer(
0, 0, texWidth, texHeight, 0, 0, texWidth, texHeight,
@ -419,11 +421,11 @@ XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, un
glGenTextures(1, &texture); glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0 + 9); glActiveTexture(GL_TEXTURE0 + 9);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, target_width, target_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, target_width, target_height, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
return new XFBSource(texture); return new XFBSource(texture, m_EFBLayers);
} }
void FramebufferManager::GetTargetSize(unsigned int *width, unsigned int *height, const EFBRectangle& sourceRc) void FramebufferManager::GetTargetSize(unsigned int *width, unsigned int *height, const EFBRectangle& sourceRc)

View File

@ -47,13 +47,14 @@ namespace OGL
struct XFBSource : public XFBSourceBase struct XFBSource : public XFBSourceBase
{ {
XFBSource(GLuint tex) : texture(tex) {} XFBSource(GLuint tex, int layers) : texture(tex), m_layers(layers) {}
~XFBSource(); ~XFBSource();
void CopyEFB(float Gamma) override; void CopyEFB(float Gamma) override;
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override; void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
const GLuint texture; const GLuint texture;
const int m_layers;
}; };
class FramebufferManager : public FramebufferManagerBase class FramebufferManager : public FramebufferManagerBase
@ -100,7 +101,6 @@ private:
static int m_msaaSamples; static int m_msaaSamples;
static GLenum m_textureType; static GLenum m_textureType;
static GLuint m_efbFramebuffer; static GLuint m_efbFramebuffer;
static GLuint m_xfbFramebuffer; static GLuint m_xfbFramebuffer;
static GLuint m_efbColor; static GLuint m_efbColor;

View File

@ -37,6 +37,8 @@ static char s_vertex_shader[] =
"}\n"; "}\n";
OpenGLPostProcessing::OpenGLPostProcessing() OpenGLPostProcessing::OpenGLPostProcessing()
: m_initialized(false)
, m_anaglyph(false)
{ {
CreateHeader(); CreateHeader();
@ -46,8 +48,6 @@ OpenGLPostProcessing::OpenGLPostProcessing()
glGenBuffers(1, &m_attribute_vbo); glGenBuffers(1, &m_attribute_vbo);
glGenVertexArrays(1, &m_attribute_vao); glGenVertexArrays(1, &m_attribute_vao);
} }
m_initialized = false;
} }
OpenGLPostProcessing::~OpenGLPostProcessing() OpenGLPostProcessing::~OpenGLPostProcessing()
@ -62,7 +62,7 @@ OpenGLPostProcessing::~OpenGLPostProcessing()
} }
void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle dst, void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle dst,
int src_texture, int src_width, int src_height) int src_texture, int src_width, int src_height, int layer)
{ {
ApplyShader(); ApplyShader();
@ -79,6 +79,7 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
glUniform4f(m_uniform_src_rect, src.left / (float) src_width, src.bottom / (float) src_height, glUniform4f(m_uniform_src_rect, src.left / (float) src_width, src.bottom / (float) src_height,
src.GetWidth() / (float) src_width, src.GetHeight() / (float) src_height); src.GetWidth() / (float) src_width, src.GetHeight() / (float) src_height);
glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed()); glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed());
glUniform1i(m_uniform_layer, layer);
if (m_config.IsDirty()) if (m_config.IsDirty())
{ {
@ -151,25 +152,29 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
} }
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, src_texture); glBindTexture(GL_TEXTURE_2D_ARRAY, src_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} }
void OpenGLPostProcessing::ApplyShader() void OpenGLPostProcessing::ApplyShader()
{ {
// shader didn't changed // shader didn't changed
if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader) if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader &&
((g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH) == m_anaglyph))
return; return;
m_shader.Destroy(); m_shader.Destroy();
m_uniform_bindings.clear(); m_uniform_bindings.clear();
// load shader from disk // load shader code
std::string default_shader = "void main() { SetOutput(Sample()); }";
std::string code = ""; std::string code = "";
if (g_ActiveConfig.sPostProcessingShader != "") std::string default_shader = "void main() { SetOutput(Sample()); }\n";
if (g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH)
code = "void main() { SetOutput(float4(pow(0.7 * SampleLayer(0).g + 0.3 * SampleLayer(0).b, 1.5), SampleLayer(1).gba)); }\n";
else if (g_ActiveConfig.sPostProcessingShader != "")
code = m_config.LoadShader(); code = m_config.LoadShader();
if (code == "") if (code == "")
@ -195,6 +200,7 @@ void OpenGLPostProcessing::ApplyShader()
m_uniform_resolution = glGetUniformLocation(m_shader.glprogid, "resolution"); m_uniform_resolution = glGetUniformLocation(m_shader.glprogid, "resolution");
m_uniform_time = glGetUniformLocation(m_shader.glprogid, "time"); m_uniform_time = glGetUniformLocation(m_shader.glprogid, "time");
m_uniform_src_rect = glGetUniformLocation(m_shader.glprogid, "src_rect"); m_uniform_src_rect = glGetUniformLocation(m_shader.glprogid, "src_rect");
m_uniform_layer = glGetUniformLocation(m_shader.glprogid, "layer");
if (m_attribute_workaround) if (m_attribute_workaround)
{ {
@ -218,6 +224,7 @@ void OpenGLPostProcessing::ApplyShader()
std::string glsl_name = "option_" + it.first; std::string glsl_name = "option_" + it.first;
m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str()); m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str());
} }
m_anaglyph = g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH;
m_initialized = true; m_initialized = true;
} }
@ -228,7 +235,7 @@ void OpenGLPostProcessing::CreateHeader()
// Shouldn't be accessed directly by the PP shader // Shouldn't be accessed directly by the PP shader
// Texture sampler // Texture sampler
"SAMPLER_BINDING(8) uniform sampler2D samp8;\n" "SAMPLER_BINDING(8) uniform sampler2D samp8;\n"
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
// Output variable // Output variable
"out float4 ocol0;\n" "out float4 ocol0;\n"
@ -238,19 +245,26 @@ void OpenGLPostProcessing::CreateHeader()
"uniform float4 resolution;\n" "uniform float4 resolution;\n"
// Time // Time
"uniform uint time;\n" "uniform uint time;\n"
// Layer
"uniform int layer;\n"
// Interfacing functions // Interfacing functions
"float4 Sample()\n" "float4 Sample()\n"
"{\n" "{\n"
"\treturn texture(samp9, uv0);\n" "\treturn texture(samp9, float3(uv0, layer));\n"
"}\n" "}\n"
"float4 SampleLocation(float2 location)\n" "float4 SampleLocation(float2 location)\n"
"{\n" "{\n"
"\treturn texture(samp9, location);\n" "\treturn texture(samp9, float3(location, layer));\n"
"}\n" "}\n"
"#define SampleOffset(offset) textureOffset(samp9, uv0, offset)\n" "float4 SampleLayer(int layer)\n"
"{\n"
"\treturn texture(samp9, float3(uv0, layer));\n"
"}\n"
"#define SampleOffset(offset) textureOffset(samp9, float3(uv0, layer), offset)\n"
"float4 SampleFontLocation(float2 location)\n" "float4 SampleFontLocation(float2 location)\n"
"{\n" "{\n"

View File

@ -23,15 +23,17 @@ public:
~OpenGLPostProcessing(); ~OpenGLPostProcessing();
void BlitFromTexture(TargetRectangle src, TargetRectangle dst, void BlitFromTexture(TargetRectangle src, TargetRectangle dst,
int src_texture, int src_width, int src_height) override; int src_texture, int src_width, int src_height, int layer) override;
void ApplyShader() override; void ApplyShader() override;
private: private:
bool m_initialized; bool m_initialized;
bool m_anaglyph;
SHADER m_shader; SHADER m_shader;
GLuint m_uniform_resolution; GLuint m_uniform_resolution;
GLuint m_uniform_src_rect; GLuint m_uniform_src_rect;
GLuint m_uniform_time; GLuint m_uniform_time;
GLuint m_uniform_layer;
std::string m_glsl_header; std::string m_glsl_header;
// These are only used when working around Qualcomm's broken attributeless rendering // These are only used when working around Qualcomm's broken attributeless rendering

View File

@ -36,6 +36,7 @@ ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
SHADERUID ProgramShaderCache::last_uid; SHADERUID ProgramShaderCache::last_uid;
UidChecker<PixelShaderUid,PixelShaderCode> ProgramShaderCache::pixel_uid_checker; UidChecker<PixelShaderUid,PixelShaderCode> ProgramShaderCache::pixel_uid_checker;
UidChecker<VertexShaderUid,VertexShaderCode> ProgramShaderCache::vertex_uid_checker; UidChecker<VertexShaderUid,VertexShaderCode> ProgramShaderCache::vertex_uid_checker;
UidChecker<GeometryShaderUid,ShaderCode> ProgramShaderCache::geometry_uid_checker;
static char s_glsl_header[1024] = ""; static char s_glsl_header[1024] = "";
@ -196,13 +197,17 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
VertexShaderCode vcode; VertexShaderCode vcode;
PixelShaderCode pcode; PixelShaderCode pcode;
ShaderCode gcode;
GenerateVertexShaderCode(vcode, components, API_OPENGL); GenerateVertexShaderCode(vcode, components, API_OPENGL);
GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components); GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components);
if (g_ActiveConfig.iStereoMode > 0)
GenerateGeometryShaderCode(gcode, components, API_OPENGL);
if (g_ActiveConfig.bEnableShaderDebugging) if (g_ActiveConfig.bEnableShaderDebugging)
{ {
newentry.shader.strvprog = vcode.GetBuffer(); newentry.shader.strvprog = vcode.GetBuffer();
newentry.shader.strpprog = pcode.GetBuffer(); newentry.shader.strpprog = pcode.GetBuffer();
newentry.shader.strgprog = gcode.GetBuffer();
} }
#if defined(_DEBUG) || defined(DEBUGFAST) #if defined(_DEBUG) || defined(DEBUGFAST)
@ -214,10 +219,16 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
filename = StringFromFormat("%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++); filename = StringFromFormat("%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
SaveData(filename, pcode.GetBuffer()); SaveData(filename, pcode.GetBuffer());
if (gcode.GetBuffer() != nullptr)
{
filename = StringFromFormat("%sgs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
SaveData(filename, gcode.GetBuffer());
}
} }
#endif #endif
if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer())) if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()))
{ {
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
return nullptr; return nullptr;
@ -231,15 +242,21 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
return &last_entry->shader; return &last_entry->shader;
} }
bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const char* pcode) bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const char* pcode, const char* gcode)
{ {
GLuint vsid = CompileSingleShader(GL_VERTEX_SHADER, vcode); GLuint vsid = CompileSingleShader(GL_VERTEX_SHADER, vcode);
GLuint psid = CompileSingleShader(GL_FRAGMENT_SHADER, pcode); GLuint psid = CompileSingleShader(GL_FRAGMENT_SHADER, pcode);
if (!vsid || !psid) // Optional geometry shader
GLuint gsid = 0;
if (gcode)
gsid = CompileSingleShader(GL_GEOMETRY_SHADER, gcode);
if (!vsid || !psid || (gcode && !gsid))
{ {
glDeleteShader(vsid); glDeleteShader(vsid);
glDeleteShader(psid); glDeleteShader(psid);
glDeleteShader(gsid);
return false; return false;
} }
@ -247,6 +264,8 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
glAttachShader(pid, vsid); glAttachShader(pid, vsid);
glAttachShader(pid, psid); glAttachShader(pid, psid);
if (gsid)
glAttachShader(pid, gsid);
if (g_ogl_config.bSupportsGLSLCache) if (g_ogl_config.bSupportsGLSLCache)
glProgramParameteri(pid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); glProgramParameteri(pid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
@ -258,6 +277,7 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
// original shaders aren't needed any more // original shaders aren't needed any more
glDeleteShader(vsid); glDeleteShader(vsid);
glDeleteShader(psid); glDeleteShader(psid);
glDeleteShader(gsid);
GLint linkStatus; GLint linkStatus;
glGetProgramiv(pid, GL_LINK_STATUS, &linkStatus); glGetProgramiv(pid, GL_LINK_STATUS, &linkStatus);
@ -273,7 +293,10 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
std::string filename = StringFromFormat("%sbad_p_%d.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); std::string filename = StringFromFormat("%sbad_p_%d.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++);
std::ofstream file; std::ofstream file;
OpenFStream(file, filename, std::ios_base::out); OpenFStream(file, filename, std::ios_base::out);
file << s_glsl_header << vcode << s_glsl_header << pcode << infoLog; file << s_glsl_header << vcode << s_glsl_header << pcode;
if (gcode)
file << s_glsl_header << gcode;
file << infoLog;
file.close(); file.close();
if (linkStatus != GL_TRUE) if (linkStatus != GL_TRUE)
@ -324,11 +347,11 @@ GLuint ProgramShaderCache::CompileSingleShader(GLuint type, const char* code)
GLsizei charsWritten; GLsizei charsWritten;
GLchar* infoLog = new GLchar[length]; GLchar* infoLog = new GLchar[length];
glGetShaderInfoLog(result, length, &charsWritten, infoLog); glGetShaderInfoLog(result, length, &charsWritten, infoLog);
ERROR_LOG(VIDEO, "%s Shader info log:\n%s", type==GL_VERTEX_SHADER ? "VS" : "PS", infoLog); ERROR_LOG(VIDEO, "%s Shader info log:\n%s", type==GL_VERTEX_SHADER ? "VS" : type==GL_FRAGMENT_SHADER ? "PS" : "GS", infoLog);
std::string filename = StringFromFormat("%sbad_%s_%04i.txt", std::string filename = StringFromFormat("%sbad_%s_%04i.txt",
File::GetUserPath(D_DUMP_IDX).c_str(), File::GetUserPath(D_DUMP_IDX).c_str(),
type==GL_VERTEX_SHADER ? "vs" : "ps", type==GL_VERTEX_SHADER ? "vs" : type==GL_FRAGMENT_SHADER ? "ps" : "gs",
num_failures++); num_failures++);
std::ofstream file; std::ofstream file;
OpenFStream(file, filename, std::ios_base::out); OpenFStream(file, filename, std::ios_base::out);
@ -338,7 +361,7 @@ GLuint ProgramShaderCache::CompileSingleShader(GLuint type, const char* code)
if (compileStatus != GL_TRUE) if (compileStatus != GL_TRUE)
{ {
PanicAlert("Failed to compile %s shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s, %s, %s):\n%s", PanicAlert("Failed to compile %s shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s, %s, %s):\n%s",
type == GL_VERTEX_SHADER ? "vertex" : "pixel", type == GL_VERTEX_SHADER ? "vertex" : type==GL_FRAGMENT_SHADER ? "pixel" : "geometry",
filename.c_str(), filename.c_str(),
g_ogl_config.gl_vendor, g_ogl_config.gl_vendor,
g_ogl_config.gl_renderer, g_ogl_config.gl_renderer,
@ -365,6 +388,7 @@ void ProgramShaderCache::GetShaderId(SHADERUID* uid, DSTALPHA_MODE dstAlphaMode,
{ {
GetPixelShaderUid(uid->puid, dstAlphaMode, API_OPENGL, components); GetPixelShaderUid(uid->puid, dstAlphaMode, API_OPENGL, components);
GetVertexShaderUid(uid->vuid, components, API_OPENGL); GetVertexShaderUid(uid->vuid, components, API_OPENGL);
GetGeometryShaderUid(uid->guid, components, API_OPENGL);
if (g_ActiveConfig.bEnableShaderDebugging) if (g_ActiveConfig.bEnableShaderDebugging)
{ {
@ -375,6 +399,10 @@ void ProgramShaderCache::GetShaderId(SHADERUID* uid, DSTALPHA_MODE dstAlphaMode,
VertexShaderCode vcode; VertexShaderCode vcode;
GenerateVertexShaderCode(vcode, components, API_OPENGL); GenerateVertexShaderCode(vcode, components, API_OPENGL);
vertex_uid_checker.AddToIndexAndCheck(vcode, uid->vuid, "Vertex", "v"); vertex_uid_checker.AddToIndexAndCheck(vcode, uid->vuid, "Vertex", "v");
ShaderCode gcode;
GenerateGeometryShaderCode(gcode, components, API_OPENGL);
geometry_uid_checker.AddToIndexAndCheck(gcode, uid->guid, "Geometry", "g");
} }
} }
@ -486,6 +514,7 @@ void ProgramShaderCache::CreateHeader()
"%s\n" // sample shading "%s\n" // sample shading
"%s\n" // Sampler binding "%s\n" // Sampler binding
"%s\n" // storage buffer "%s\n" // storage buffer
"%s\n" // shader5
// Precision defines for GLSL ES // Precision defines for GLSL ES
"%s\n" "%s\n"
@ -518,6 +547,7 @@ void ProgramShaderCache::CreateHeader()
, (g_ogl_config.bSupportSampleShading) ? "#extension GL_ARB_sample_shading : enable" : "" , (g_ogl_config.bSupportSampleShading) ? "#extension GL_ARB_sample_shading : enable" : ""
, g_ActiveConfig.backend_info.bSupportsBindingLayout ? "#define SAMPLER_BINDING(x) layout(binding = x)" : "#define SAMPLER_BINDING(x)" , g_ActiveConfig.backend_info.bSupportsBindingLayout ? "#define SAMPLER_BINDING(x) layout(binding = x)" : "#define SAMPLER_BINDING(x)"
, g_ActiveConfig.backend_info.bSupportsBBox ? "#extension GL_ARB_shader_storage_buffer_object : enable" : "" , g_ActiveConfig.backend_info.bSupportsBBox ? "#extension GL_ARB_shader_storage_buffer_object : enable" : ""
, g_ActiveConfig.backend_info.bSupportsGSInstancing ? "#extension GL_ARB_gpu_shader5 : enable" : ""
, v>=GLSLES_300 ? "precision highp float;" : "" , v>=GLSLES_300 ? "precision highp float;" : ""
, v>=GLSLES_300 ? "precision highp int;" : "" , v>=GLSLES_300 ? "precision highp int;" : ""

View File

@ -7,6 +7,7 @@
#include "Common/LinearDiskCache.h" #include "Common/LinearDiskCache.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "VideoBackends/OGL/GLUtil.h" #include "VideoBackends/OGL/GLUtil.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PixelShaderGen.h" #include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/VertexShaderGen.h" #include "VideoCommon/VertexShaderGen.h"
@ -18,10 +19,11 @@ class SHADERUID
public: public:
VertexShaderUid vuid; VertexShaderUid vuid;
PixelShaderUid puid; PixelShaderUid puid;
GeometryShaderUid guid;
SHADERUID() {} SHADERUID() {}
SHADERUID(const SHADERUID& r) : vuid(r.vuid), puid(r.puid) {} SHADERUID(const SHADERUID& r) : vuid(r.vuid), puid(r.puid), guid(r.guid) {}
bool operator <(const SHADERUID& r) const bool operator <(const SHADERUID& r) const
{ {
@ -34,12 +36,18 @@ public:
if (vuid < r.vuid) if (vuid < r.vuid)
return true; return true;
if (r.vuid < vuid)
return false;
if (guid < r.guid)
return true;
return false; return false;
} }
bool operator ==(const SHADERUID& r) const bool operator ==(const SHADERUID& r) const
{ {
return puid == r.puid && vuid == r.vuid; return puid == r.puid && vuid == r.vuid && guid == r.guid;
} }
}; };
@ -54,7 +62,7 @@ struct SHADER
} }
GLuint glprogid; // OpenGL program id GLuint glprogid; // OpenGL program id
std::string strvprog, strpprog; std::string strvprog, strpprog, strgprog;
void SetProgramVariables(); void SetProgramVariables();
void SetProgramBindings(); void SetProgramBindings();
@ -83,7 +91,7 @@ public:
static SHADER* SetShader(DSTALPHA_MODE dstAlphaMode, u32 components); static SHADER* SetShader(DSTALPHA_MODE dstAlphaMode, u32 components);
static void GetShaderId(SHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 components); static void GetShaderId(SHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 components);
static bool CompileShader(SHADER &shader, const char* vcode, const char* pcode); static bool CompileShader(SHADER &shader, const char* vcode, const char* pcode, const char* gcode = nullptr);
static GLuint CompileSingleShader(GLuint type, const char *code); static GLuint CompileSingleShader(GLuint type, const char *code);
static void UploadConstants(); static void UploadConstants();
@ -104,6 +112,7 @@ private:
static UidChecker<PixelShaderUid,PixelShaderCode> pixel_uid_checker; static UidChecker<PixelShaderUid,PixelShaderCode> pixel_uid_checker;
static UidChecker<VertexShaderUid,VertexShaderCode> vertex_uid_checker; static UidChecker<VertexShaderUid,VertexShaderCode> vertex_uid_checker;
static UidChecker<GeometryShaderUid,ShaderCode> geometry_uid_checker;
static u32 s_ubo_buffer_size; static u32 s_ubo_buffer_size;
static s32 s_ubo_align; static s32 s_ubo_align;

View File

@ -89,6 +89,8 @@ static RasterFont* s_pfont = nullptr;
static int s_MSAASamples = 1; static int s_MSAASamples = 1;
static int s_LastMultisampleMode = 0; static int s_LastMultisampleMode = 0;
static bool s_LastStereo = false;
static u32 s_blendMode; static u32 s_blendMode;
static bool s_vsync; static bool s_vsync;
@ -204,6 +206,10 @@ static void GLAPIENTRY ClearDepthf(GLfloat depthval)
{ {
glClearDepth(depthval); glClearDepth(depthval);
} }
static void GLAPIENTRY FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level)
{
glFramebufferTextureLayer(target, attachment, texture, level, 0);
}
static void InitDriverInfo() static void InitDriverInfo()
{ {
@ -460,11 +466,17 @@ Renderer::Renderer()
glClearDepthf = ClearDepthf; glClearDepthf = ClearDepthf;
} }
if (GLExtensions::Version() < 320 || GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
{
glFramebufferTexture = FramebufferTexture;
}
g_Config.backend_info.bSupportsDualSourceBlend = GLExtensions::Supports("GL_ARB_blend_func_extended"); g_Config.backend_info.bSupportsDualSourceBlend = GLExtensions::Supports("GL_ARB_blend_func_extended");
g_Config.backend_info.bSupportsPrimitiveRestart = !DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVERESTART) && g_Config.backend_info.bSupportsPrimitiveRestart = !DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVERESTART) &&
((GLExtensions::Version() >= 310) || GLExtensions::Supports("GL_NV_primitive_restart")); ((GLExtensions::Version() >= 310) || GLExtensions::Supports("GL_NV_primitive_restart"));
g_Config.backend_info.bSupportsEarlyZ = GLExtensions::Supports("GL_ARB_shader_image_load_store"); g_Config.backend_info.bSupportsEarlyZ = GLExtensions::Supports("GL_ARB_shader_image_load_store");
g_Config.backend_info.bSupportsBBox = GLExtensions::Supports("GL_ARB_shader_storage_buffer_object"); g_Config.backend_info.bSupportsBBox = GLExtensions::Supports("GL_ARB_shader_storage_buffer_object");
g_Config.backend_info.bSupportsGSInstancing = GLExtensions::Supports("GL_ARB_gpu_shader5");
// Desktop OpenGL supports the binding layout if it supports 420pack // Desktop OpenGL supports the binding layout if it supports 420pack
// OpenGL ES 3.1 supports it implicitly without an extension // OpenGL ES 3.1 supports it implicitly without an extension
@ -493,6 +505,8 @@ Renderer::Renderer()
g_Config.backend_info.bSupportsBindingLayout = true; g_Config.backend_info.bSupportsBindingLayout = true;
g_Config.backend_info.bSupportsEarlyZ = true; g_Config.backend_info.bSupportsEarlyZ = true;
} }
// TODO: OpenGL ES 3.1 provides the necessary features as extensions.
g_Config.backend_info.bSupportsStereoscopy = false;
} }
else else
{ {
@ -507,11 +521,13 @@ Renderer::Renderer()
{ {
g_ogl_config.eSupportedGLSLVersion = GLSL_130; g_ogl_config.eSupportedGLSLVersion = GLSL_130;
g_Config.backend_info.bSupportsEarlyZ = false; // layout keyword is only supported on glsl150+ g_Config.backend_info.bSupportsEarlyZ = false; // layout keyword is only supported on glsl150+
g_Config.backend_info.bSupportsStereoscopy = false; // geometry shaders are only supported on glsl150+
} }
else if (strstr(g_ogl_config.glsl_version, "1.40")) else if (strstr(g_ogl_config.glsl_version, "1.40"))
{ {
g_ogl_config.eSupportedGLSLVersion = GLSL_140; g_ogl_config.eSupportedGLSLVersion = GLSL_140;
g_Config.backend_info.bSupportsEarlyZ = false; // layout keyword is only supported on glsl150+ g_Config.backend_info.bSupportsEarlyZ = false; // layout keyword is only supported on glsl150+
g_Config.backend_info.bSupportsStereoscopy = false; // geometry shaders are only supported on glsl150+
} }
else else
{ {
@ -545,6 +561,9 @@ Renderer::Renderer()
bSuccess = false; bSuccess = false;
} }
if (g_Config.iStereoMode > 0 && !g_Config.backend_info.bSupportsStereoscopy)
OSD::AddMessage("Stereoscopic 3D isn't supported by your GPU, support for OpenGL 3.2 is required.", 10000);
if (!bSuccess) if (!bSuccess)
{ {
// Not all needed extensions are supported, so we have to stop here. // Not all needed extensions are supported, so we have to stop here.
@ -556,6 +575,7 @@ Renderer::Renderer()
if (g_ogl_config.max_samples < 1 || !g_ogl_config.bSupportsMSAA) if (g_ogl_config.max_samples < 1 || !g_ogl_config.bSupportsMSAA)
g_ogl_config.max_samples = 1; g_ogl_config.max_samples = 1;
g_Config.VerifyValidity();
UpdateActiveConfig(); UpdateActiveConfig();
OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s",
@ -563,7 +583,7 @@ Renderer::Renderer()
g_ogl_config.gl_renderer, g_ogl_config.gl_renderer,
g_ogl_config.gl_version), 5000); g_ogl_config.gl_version), 5000);
WARN_LOG(VIDEO,"Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s", WARN_LOG(VIDEO,"Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s",
g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ", g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ",
g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? "" : "PrimitiveRestart ", g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? "" : "PrimitiveRestart ",
g_ActiveConfig.backend_info.bSupportsEarlyZ ? "" : "EarlyZ ", g_ActiveConfig.backend_info.bSupportsEarlyZ ? "" : "EarlyZ ",
@ -573,12 +593,14 @@ Renderer::Renderer()
g_ogl_config.bSupportsGLBufferStorage ? "" : "BufferStorage ", g_ogl_config.bSupportsGLBufferStorage ? "" : "BufferStorage ",
g_ogl_config.bSupportsGLSync ? "" : "Sync ", g_ogl_config.bSupportsGLSync ? "" : "Sync ",
g_ogl_config.bSupportsMSAA ? "" : "MSAA ", g_ogl_config.bSupportsMSAA ? "" : "MSAA ",
g_ogl_config.bSupportSampleShading ? "" : "SSAA " g_ogl_config.bSupportSampleShading ? "" : "SSAA ",
g_ActiveConfig.backend_info.bSupportsGSInstancing ? "" : "GSInstancing "
); );
s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode; s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;
s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode); s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode);
ApplySSAASettings(); ApplySSAASettings();
s_LastStereo = g_ActiveConfig.iStereoMode > 0;
// Decide framebuffer size // Decide framebuffer size
s_backbuffer_width = (int)GLInterface->GetBackBufferWidth(); s_backbuffer_width = (int)GLInterface->GetBackBufferWidth();
@ -1493,7 +1515,8 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
sourceRc.right -= fbStride - fbWidth; sourceRc.right -= fbStride - fbWidth;
m_post_processor->BlitFromTexture(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight); // TODO: Virtual XFB stereoscopic 3D support.
m_post_processor->BlitFromTexture(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
} }
} }
else else
@ -1503,7 +1526,18 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
// for msaa mode, we must resolve the efb content to non-msaa // for msaa mode, we must resolve the efb content to non-msaa
GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc); GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
m_post_processor->BlitFromTexture(targetRc, flipped_trc, tex, s_target_width, s_target_height); if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB)
{
TargetRectangle leftRc, rightRc;
ConvertStereoRectangle(flipped_trc, leftRc, rightRc);
m_post_processor->BlitFromTexture(targetRc, leftRc, tex, s_target_width, s_target_height, 0);
m_post_processor->BlitFromTexture(targetRc, rightRc, tex, s_target_width, s_target_height, 1);
}
else
{
m_post_processor->BlitFromTexture(targetRc, flipped_trc, tex, s_target_width, s_target_height);
}
} }
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
@ -1652,15 +1686,16 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
s_LastEFBScale = g_ActiveConfig.iEFBScale; s_LastEFBScale = g_ActiveConfig.iEFBScale;
} }
if (xfbchanged || WindowResized || (s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode)) if (xfbchanged || WindowResized || (s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode) || (s_LastStereo != (g_ActiveConfig.iStereoMode > 0)))
{ {
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode) if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode || s_LastStereo != (g_ActiveConfig.iStereoMode > 0))
{ {
s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode; s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;
s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode); s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode);
ApplySSAASettings(); ApplySSAASettings();
s_LastStereo = g_ActiveConfig.iStereoMode > 0;
delete g_framebuffer_manager; delete g_framebuffer_manager;
g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height, g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height,

View File

@ -98,14 +98,14 @@ void TextureCache::TCacheEntry::Bind(unsigned int stage)
s_ActiveTexture = stage; s_ActiveTexture = stage;
} }
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
s_Textures[stage] = texture; s_Textures[stage] = texture;
} }
} }
bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level) bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level)
{ {
return SaveTexture(filename, GL_TEXTURE_2D, texture, virtual_width, virtual_height, level); return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, texture, virtual_width, virtual_height, level);
} }
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
@ -172,8 +172,8 @@ TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
entry.pcfmt = pcfmt; entry.pcfmt = pcfmt;
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, entry.texture); glBindTexture(GL_TEXTURE_2D_ARRAY, entry.texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, tex_levels - 1); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, tex_levels - 1);
entry.Load(width, height, expanded_width, 0); entry.Load(width, height, expanded_width, 0);
@ -189,12 +189,12 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
if (pcfmt != PC_TEX_FMT_DXT1) if (pcfmt != PC_TEX_FMT_DXT1)
{ {
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
if (expanded_width != width) if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width); glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width);
glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp); glTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_iformat, width, height, 1, 0, gl_format, gl_type, temp);
if (expanded_width != width) if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@ -213,21 +213,20 @@ TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
{ {
TCacheEntry *const entry = new TCacheEntry; TCacheEntry *const entry = new TCacheEntry;
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, entry->texture); glBindTexture(GL_TEXTURE_2D_ARRAY, entry->texture);
const GLenum const GLenum
gl_format = GL_RGBA, gl_format = GL_RGBA,
gl_iformat = GL_RGBA, gl_iformat = GL_RGBA,
gl_type = GL_UNSIGNED_BYTE; gl_type = GL_UNSIGNED_BYTE;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, FramebufferManager::GetEFBLayers(), 0, gl_format, gl_type, nullptr);
glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, scaled_tex_w, scaled_tex_h, 0, gl_format, gl_type, nullptr); glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &entry->framebuffer); glGenFramebuffers(1, &entry->framebuffer);
FramebufferManager::SetFramebuffer(entry->framebuffer); FramebufferManager::SetFramebuffer(entry->framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, entry->texture, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, entry->texture, 0);
SetStage(); SetStage();
@ -251,7 +250,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
FramebufferManager::SetFramebuffer(framebuffer); FramebufferManager::SetFramebuffer(framebuffer);
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, read_texture); glBindTexture(GL_TEXTURE_2D_ARRAY, read_texture);
glViewport(0, 0, virtual_width, virtual_height); glViewport(0, 0, virtual_width, virtual_height);
@ -309,34 +308,60 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
{ {
static int count = 0; static int count = 0;
SaveTexture(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), SaveTexture(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(),
count++), GL_TEXTURE_2D, texture, virtual_width, virtual_height, 0); count++), GL_TEXTURE_2D_ARRAY, texture, virtual_width, virtual_height, 0);
} }
g_renderer->RestoreAPIState(); g_renderer->RestoreAPIState();
} }
TextureCache::TextureCache() TextureCache::TextureCache()
{
CompileShaders();
s_ActiveTexture = -1;
for (auto& gtex : s_Textures)
gtex = -1;
}
TextureCache::~TextureCache()
{
DeleteShaders();
}
void TextureCache::DisableStage(unsigned int stage)
{
}
void TextureCache::SetStage()
{
// -1 is the initial value as we don't know which texture should be bound
if (s_ActiveTexture != (u32)-1)
glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
}
void TextureCache::CompileShaders()
{ {
const char *pColorMatrixProg = const char *pColorMatrixProg =
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 colmat[7];\n" "uniform vec4 colmat[7];\n"
"in vec2 uv0;\n" "in vec3 f_uv0;\n"
"out vec4 ocol0;\n" "out vec4 ocol0;\n"
"\n" "\n"
"void main(){\n" "void main(){\n"
" vec4 texcol = texture(samp9, uv0);\n" " vec4 texcol = texture(samp9, f_uv0);\n"
" texcol = round(texcol * colmat[5]) * colmat[6];\n" " texcol = round(texcol * colmat[5]) * colmat[6];\n"
" ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n" " ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
"}\n"; "}\n";
const char *pDepthMatrixProg = const char *pDepthMatrixProg =
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 colmat[5];\n" "uniform vec4 colmat[5];\n"
"in vec2 uv0;\n" "in vec3 f_uv0;\n"
"out vec4 ocol0;\n" "out vec4 ocol0;\n"
"\n" "\n"
"void main(){\n" "void main(){\n"
" vec4 texcol = texture(samp9, uv0);\n" " vec4 texcol = texture(samp9, vec3(f_uv0.xy, %s));\n"
// 255.99998474121 = 16777215/16777216*256 // 255.99998474121 = 16777215/16777216*256
" float workspace = texcol.x * 255.99998474121;\n" " float workspace = texcol.x * 255.99998474121;\n"
@ -363,18 +388,41 @@ TextureCache::TextureCache()
"}\n"; "}\n";
const char *VProgram = const char *VProgram =
"out vec2 uv0;\n" "out vec3 %s_uv0;\n"
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 copy_position;\n" // left, top, right, bottom "uniform vec4 copy_position;\n" // left, top, right, bottom
"void main()\n" "void main()\n"
"{\n" "{\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n" " vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0));\n" " %s_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy), 0.0);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n" " gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
"}\n"; "}\n";
ProgramShaderCache::CompileShader(s_ColorMatrixProgram, VProgram, pColorMatrixProg); const char *GProgram = (g_ActiveConfig.iStereoMode > 0) ?
ProgramShaderCache::CompileShader(s_DepthMatrixProgram, VProgram, pDepthMatrixProg); "layout(triangles) in;\n"
"layout(triangle_strip, max_vertices = 6) out;\n"
"in vec3 v_uv0[3];\n"
"out vec3 f_uv0;\n"
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"void main()\n"
"{\n"
" int layers = textureSize(samp9, 0).z;\n"
" for (int layer = 0; layer < layers; ++layer) {\n"
" for (int i = 0; i < 3; ++i) {\n"
" f_uv0 = vec3(v_uv0[i].xy, layer);\n"
" gl_Position = gl_in[i].gl_Position;\n"
" gl_Layer = layer;\n"
" EmitVertex();\n"
" }\n"
" EndPrimitive();\n"
" }\n"
"}\n" : nullptr;
const char* prefix = (GProgram == nullptr) ? "f" : "v";
const char* depth_layer = (g_ActiveConfig.bStereoMonoEFBDepth) ? "0" : "f_uv0.z";
ProgramShaderCache::CompileShader(s_ColorMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), pColorMatrixProg, GProgram);
ProgramShaderCache::CompileShader(s_DepthMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), StringFromFormat(pDepthMatrixProg, depth_layer).c_str(), GProgram);
s_ColorMatrixUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "colmat"); s_ColorMatrixUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "colmat");
s_DepthMatrixUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "colmat"); s_DepthMatrixUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "colmat");
@ -383,28 +431,12 @@ TextureCache::TextureCache()
s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position"); s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position");
s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position"); s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position");
s_ActiveTexture = -1;
for (auto& gtex : s_Textures)
gtex = -1;
} }
void TextureCache::DeleteShaders()
TextureCache::~TextureCache()
{ {
s_ColorMatrixProgram.Destroy(); s_ColorMatrixProgram.Destroy();
s_DepthMatrixProgram.Destroy(); s_DepthMatrixProgram.Destroy();
} }
void TextureCache::DisableStage(unsigned int stage)
{
}
void TextureCache::SetStage ()
{
// -1 is the initial value as we don't know which texture should be bound
if (s_ActiveTexture != (u32)-1)
glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
}
} }

View File

@ -57,6 +57,9 @@ private:
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) override; unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) override;
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) override; TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) override;
void CompileShaders() override;
void DeleteShaders() override;
}; };
bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, int virtual_height, unsigned int level); bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, int virtual_height, unsigned int level);

View File

@ -72,21 +72,21 @@ static void CreatePrograms()
const char *VProgramRgbToYuyv = const char *VProgramRgbToYuyv =
"out vec2 uv0;\n" "out vec2 uv0;\n"
"uniform vec4 copy_position;\n" // left, top, right, bottom "uniform vec4 copy_position;\n" // left, top, right, bottom
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n" " vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n" " gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
" uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0));\n" " uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
"}\n"; "}\n";
const char *FProgramRgbToYuyv = const char *FProgramRgbToYuyv =
"SAMPLER_BINDING(9) uniform sampler2D samp9;\n" "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"in vec2 uv0;\n" "in vec2 uv0;\n"
"out vec4 ocol0;\n" "out vec4 ocol0;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" vec3 c0 = texture(samp9, (uv0 - dFdx(uv0) * 0.25)).rgb;\n" " vec3 c0 = texture(samp9, vec3(uv0 - dFdx(uv0) * 0.25, 0.0)).rgb;\n"
" vec3 c1 = texture(samp9, (uv0 + dFdx(uv0) * 0.25)).rgb;\n" " vec3 c1 = texture(samp9, vec3(uv0 + dFdx(uv0) * 0.25, 0.0)).rgb;\n"
" vec3 c01 = (c0 + c1) * 0.5;\n" " vec3 c01 = (c0 + c1) * 0.5;\n"
" vec3 y_const = vec3(0.257,0.504,0.098);\n" " vec3 y_const = vec3(0.257,0.504,0.098);\n"
" vec3 u_const = vec3(-0.148,-0.291,0.439);\n" " vec3 u_const = vec3(-0.148,-0.291,0.439);\n"
@ -224,17 +224,17 @@ static void EncodeToRamUsingShader(GLuint srcTexture,
// set source texture // set source texture
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, srcTexture); glBindTexture(GL_TEXTURE_2D_ARRAY, srcTexture);
if (linearFilter) if (linearFilter)
{ {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} }
else else
{ {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} }
glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight); glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight);
@ -365,7 +365,7 @@ void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTextur
// switch to texture converter frame buffer // switch to texture converter frame buffer
// attach destTexture as color destination // attach destTexture as color destination
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[1]); FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[1]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destTexture, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, destTexture, 0);
// activate source texture // activate source texture
// set srcAddr as data for source texture // set srcAddr as data for source texture

View File

@ -138,6 +138,7 @@ static void InitBackendInfo()
//g_Config.backend_info.bSupportsDualSourceBlend = true; // is gpu dependent and must be set in renderer //g_Config.backend_info.bSupportsDualSourceBlend = true; // is gpu dependent and must be set in renderer
//g_Config.backend_info.bSupportsEarlyZ = true; // is gpu dependent and must be set in renderer //g_Config.backend_info.bSupportsEarlyZ = true; // is gpu dependent and must be set in renderer
g_Config.backend_info.bSupportsOversizedViewports = true; g_Config.backend_info.bSupportsOversizedViewports = true;
g_Config.backend_info.bSupportsStereoscopy = true;
g_Config.backend_info.Adapters.clear(); g_Config.backend_info.Adapters.clear();

View File

@ -9,6 +9,7 @@ set(SRCS BoundingBox.cpp
Fifo.cpp Fifo.cpp
FPSCounter.cpp FPSCounter.cpp
FramebufferManagerBase.cpp FramebufferManagerBase.cpp
GeometryShaderGen.cpp
HiresTextures.cpp HiresTextures.cpp
ImageWrite.cpp ImageWrite.cpp
IndexGenerator.cpp IndexGenerator.cpp

View File

@ -43,5 +43,6 @@ struct VertexShaderConstants
float4 normalmatrices[32]; float4 normalmatrices[32];
float4 posttransformmatrices[64]; float4 posttransformmatrices[64];
float4 depthparams; float4 depthparams;
float4 stereoparams;
}; };

View File

@ -12,6 +12,8 @@ const XFBSourceBase* FramebufferManagerBase::m_overlappingXFBArray[MAX_VIRTUAL_X
unsigned int FramebufferManagerBase::s_last_xfb_width = 1; unsigned int FramebufferManagerBase::s_last_xfb_width = 1;
unsigned int FramebufferManagerBase::s_last_xfb_height = 1; unsigned int FramebufferManagerBase::s_last_xfb_height = 1;
unsigned int FramebufferManagerBase::m_EFBLayers = 1;
FramebufferManagerBase::FramebufferManagerBase() FramebufferManagerBase::FramebufferManagerBase()
{ {
m_realXFBSource = nullptr; m_realXFBSource = nullptr;

View File

@ -55,6 +55,8 @@ public:
static int ScaleToVirtualXfbWidth(int x, unsigned int backbuffer_width); static int ScaleToVirtualXfbWidth(int x, unsigned int backbuffer_width);
static int ScaleToVirtualXfbHeight(int y, unsigned int backbuffer_height); static int ScaleToVirtualXfbHeight(int y, unsigned int backbuffer_height);
static int GetEFBLayers() { return m_EFBLayers; }
protected: protected:
struct VirtualXFB struct VirtualXFB
{ {
@ -70,6 +72,8 @@ protected:
typedef std::list<VirtualXFB> VirtualXFBListType; typedef std::list<VirtualXFB> VirtualXFBListType;
static unsigned int m_EFBLayers;
private: private:
virtual XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height) = 0; virtual XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height) = 0;
// TODO: figure out why OGL is different for this guy // TODO: figure out why OGL is different for this guy

View File

@ -0,0 +1,134 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <cmath>
#include <locale.h>
#ifdef __APPLE__
#include <xlocale.h>
#endif
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/LightingShaderGen.h"
#include "VideoCommon/VertexShaderGen.h"
#include "VideoCommon/VideoConfig.h"
static char text[16384];
template<class T>
static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiType)
{
// Non-uid template parameters will write to the dummy data (=> gets optimized out)
geometry_shader_uid_data dummy_data;
geometry_shader_uid_data* uid_data = out.template GetUidData<geometry_shader_uid_data>();
if (uid_data == nullptr)
uid_data = &dummy_data;
out.SetBuffer(text);
const bool is_writing_shadercode = (out.GetBuffer() != nullptr);
#ifndef ANDROID
locale_t locale;
locale_t old_locale;
if (is_writing_shadercode)
{
locale = newlocale(LC_NUMERIC_MASK, "C", nullptr); // New locale for compilation
old_locale = uselocale(locale); // Apply the locale for this thread
}
#endif
if (is_writing_shadercode)
text[sizeof(text) - 1] = 0x7C; // canary
out.Write("//Geometry Shader for 3D stereoscopy\n");
uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
if (ApiType == API_OPENGL)
{
// Insert layout parameters
if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
else
out.Write("layout(triangles) in;\n");
out.Write("layout(triangle_strip, max_vertices = %d) out;\n", g_ActiveConfig.backend_info.bSupportsGSInstancing ? 3 : 6);
}
out.Write("%s", s_lighting_struct);
// uniforms
if (ApiType == API_OPENGL)
out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : "");
else
out.Write("cbuffer VSBlock {\n");
out.Write(s_shader_uniforms);
out.Write("};\n");
uid_data->numTexGens = xfmem.numTexGen.numTexGens;
uid_data->pixel_lighting = g_ActiveConfig.bEnablePixelLighting;
GenerateVSOutputStruct(out, ApiType);
out.Write("centroid in VS_OUTPUT o[3];\n");
out.Write("centroid out VS_OUTPUT f;\n");
out.Write("flat out int layer;\n");
out.Write("void main()\n{\n");
// If the GPU supports invocation we don't need a for loop and can simply use the
// invocation identifier to determine which layer we're rendering.
if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
out.Write("\tint l = gl_InvocationID;\n");
else
out.Write("\tfor (int l = 0; l < %d; ++l) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
out.Write("\tfor (int i = 0; i < 3; ++i) {\n");
out.Write("\t\tlayer = l;\n");
out.Write("\t\tgl_Layer = l;\n");
out.Write("\t\tf = o[i];\n");
out.Write("\t\tfloat4 pos = o[i].pos;\n");
if (g_ActiveConfig.iStereoMode > 0)
{
// For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional
// to the depth of the vertex. We retrieve the depth value from the w-component of the projected
// vertex which contains the negated z-component of the original vertex.
// For negative parallax (out-of-screen effects) we subtract a convergence value from
// the depth value. This results in objects at a distance smaller than the convergence
// distance to seemingly appear in front of the screen.
// This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide"
out.Write("\t\tf.clipPos.x = o[i].clipPos.x + " I_STEREOPARAMS"[l] * (o[i].clipPos.w - " I_STEREOPARAMS"[2]);\n");
out.Write("\t\tpos.x = o[i].pos.x + " I_STEREOPARAMS"[l] * (o[i].pos.w - " I_STEREOPARAMS"[2]);\n");
}
out.Write("\t\tf.pos.x = pos.x;\n");
out.Write("\t\tgl_Position = pos;\n");
out.Write("\t\tEmitVertex();\n");
out.Write("\t}\n");
out.Write("\tEndPrimitive();\n");
if (!g_ActiveConfig.backend_info.bSupportsGSInstancing)
out.Write("\t}\n");
out.Write("}\n");
if (is_writing_shadercode)
{
if (text[sizeof(text) - 1] != 0x7C)
PanicAlert("GeometryShader generator - buffer too small, canary has been eaten!");
#ifndef ANDROID
uselocale(old_locale); // restore locale
freelocale(locale);
#endif
}
}
void GetGeometryShaderUid(GeometryShaderUid& object, u32 components, API_TYPE ApiType)
{
GenerateGeometryShader<GeometryShaderUid>(object, components, ApiType);
}
void GenerateGeometryShaderCode(ShaderCode& object, u32 components, API_TYPE ApiType)
{
GenerateGeometryShader<ShaderCode>(object, components, ApiType);
}

View File

@ -0,0 +1,26 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/VideoCommon.h"
#pragma pack(1)
struct geometry_shader_uid_data
{
u32 NumValues() const { return sizeof(geometry_shader_uid_data); }
u32 stereo : 1;
u32 numTexGens : 4;
u32 pixel_lighting : 1;
};
#pragma pack()
typedef ShaderUid<geometry_shader_uid_data> GeometryShaderUid;
void GenerateGeometryShaderCode(ShaderCode& object, u32 components, API_TYPE ApiType);
void GetGeometryShaderUid(GeometryShaderUid& object, u32 components, API_TYPE ApiType);

View File

@ -16,6 +16,7 @@
#include "VideoCommon/LightingShaderGen.h" #include "VideoCommon/LightingShaderGen.h"
#include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/PixelShaderGen.h" #include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/VertexShaderGen.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
#include "VideoCommon/XFMemory.h" // for texture projection mode #include "VideoCommon/XFMemory.h" // for texture projection mode
@ -206,7 +207,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
{ {
// Declare samplers // Declare samplers
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
out.Write("SAMPLER_BINDING(%d) uniform sampler2D samp%d;\n", i, i); out.Write("SAMPLER_BINDING(%d) uniform sampler2DArray samp%d;\n", i, i);
} }
else // D3D else // D3D
{ {
@ -253,17 +254,8 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
{ {
out.Write("cbuffer VSBlock : register(b1) {\n"); out.Write("cbuffer VSBlock : register(b1) {\n");
} }
out.Write( out.Write(s_shader_uniforms);
"\tfloat4 " I_POSNORMALMATRIX"[6];\n" out.Write("};\n");
"\tfloat4 " I_PROJECTION"[4];\n"
"\tint4 " I_MATERIALS"[4];\n"
"\tLight " I_LIGHTS"[8];\n"
"\tfloat4 " I_TEXMATRICES"[24];\n"
"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
"\tfloat4 " I_NORMALMATRICES"[32];\n"
"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
"\tfloat4 " I_DEPTHPARAMS";\n"
"};\n");
} }
if (g_ActiveConfig.backend_info.bSupportsBBox) if (g_ActiveConfig.backend_info.bSupportsBBox)
@ -275,6 +267,8 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
); );
} }
GenerateVSOutputStruct(out, ApiType);
const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED); const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED);
const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) || (!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z); const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) || (!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z);
@ -325,22 +319,52 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
// As a workaround, we interpolate at the centroid of the coveraged pixel, which // As a workaround, we interpolate at the centroid of the coveraged pixel, which
// is always inside the primitive. // is always inside the primitive.
// Without MSAA, this flag is defined to have no effect. // Without MSAA, this flag is defined to have no effect.
out.Write("centroid in float4 colors_02;\n"); uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
out.Write("centroid in float4 colors_12;\n"); if (g_ActiveConfig.iStereoMode > 0)
// compute window position if needed because binding semantic WPOS is not widely supported
// Let's set up attributes
for (unsigned int i = 0; i < numTexgen; ++i)
{ {
out.Write("centroid in float3 uv%d;\n", i); out.Write("centroid in VS_OUTPUT f;\n");
out.Write("flat in int layer;\n");
} }
out.Write("centroid in float4 clipPos;\n"); else
if (g_ActiveConfig.bEnablePixelLighting)
{ {
out.Write("centroid in float4 Normal;\n"); out.Write("centroid in float4 colors_02;\n");
out.Write("centroid in float4 colors_12;\n");
// compute window position if needed because binding semantic WPOS is not widely supported
// Let's set up attributes
for (unsigned int i = 0; i < numTexgen; ++i)
{
out.Write("centroid in float3 uv%d;\n", i);
}
out.Write("centroid in float4 clipPos;\n");
if (g_ActiveConfig.bEnablePixelLighting)
{
out.Write("centroid in float4 Normal;\n");
}
} }
out.Write("void main()\n{\n"); out.Write("void main()\n{\n");
if (g_ActiveConfig.iStereoMode > 0)
{
// compute window position if needed because binding semantic WPOS is not widely supported
// Let's set up attributes
for (unsigned int i = 0; i < numTexgen; ++i)
{
out.Write("\tfloat3 uv%d = f.tex%d;\n", i, i);
}
out.Write("\tfloat4 clipPos = f.clipPos;\n");
if (g_ActiveConfig.bEnablePixelLighting)
{
out.Write("\tfloat4 Normal = f.Normal;\n");
}
}
// On Mali, global variables must be initialized as constants.
// This is why we initialize these variables locally instead.
out.Write("\tfloat4 colors_0 = %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "f.colors_0" : "colors_02");
out.Write("\tfloat4 colors_1 = %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "f.colors_1" : "colors_12");
out.Write("\tfloat4 rawpos = gl_FragCoord;\n"); out.Write("\tfloat4 rawpos = gl_FragCoord;\n");
} }
else // D3D else // D3D
@ -370,14 +394,6 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
"\tint2 wrappedcoord=int2(0,0), tempcoord=int2(0,0);\n" "\tint2 wrappedcoord=int2(0,0), tempcoord=int2(0,0);\n"
"\tint4 tevin_a=int4(0,0,0,0),tevin_b=int4(0,0,0,0),tevin_c=int4(0,0,0,0),tevin_d=int4(0,0,0,0);\n\n"); // tev combiner inputs "\tint4 tevin_a=int4(0,0,0,0),tevin_b=int4(0,0,0,0),tevin_c=int4(0,0,0,0),tevin_d=int4(0,0,0,0);\n\n"); // tev combiner inputs
if (ApiType == API_OPENGL)
{
// On Mali, global variables must be initialized as constants.
// This is why we initialize these variables locally instead.
out.Write("\tfloat4 colors_0 = colors_02;\n");
out.Write("\tfloat4 colors_1 = colors_12;\n");
}
if (g_ActiveConfig.bEnablePixelLighting) if (g_ActiveConfig.bEnablePixelLighting)
{ {
out.Write("\tfloat3 _norm0 = normalize(Normal.xyz);\n\n"); out.Write("\tfloat3 _norm0 = normalize(Normal.xyz);\n\n");
@ -931,7 +947,7 @@ static inline void SampleTexture(T& out, const char *texcoords, const char *texs
if (ApiType == API_D3D) if (ApiType == API_D3D)
out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap); out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap);
else else
out.Write("iround(255.0 * texture(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap, texcoords, texmap, texswap); out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.iStereoMode > 0 ? "layer" : "0.0", texswap);
} }
static const char *tevAlphaFuncsTable[] = static const char *tevAlphaFuncsTable[] =

View File

@ -44,7 +44,7 @@ struct pixel_shader_uid_data
u32 dstAlphaMode : 2; u32 dstAlphaMode : 2;
u32 Pretest : 2; u32 Pretest : 2;
u32 nIndirectStagesUsed : 4; u32 nIndirectStagesUsed : 4;
u32 pad0 : 1; u32 stereo : 1;
u32 genMode_numtexgens : 4; u32 genMode_numtexgens : 4;
u32 genMode_numtevstages : 4; u32 genMode_numtevstages : 4;

View File

@ -91,7 +91,7 @@ public:
// Should be implemented by the backends for backend specific code // Should be implemented by the backends for backend specific code
virtual void BlitFromTexture(TargetRectangle src, TargetRectangle dst, virtual void BlitFromTexture(TargetRectangle src, TargetRectangle dst,
int src_texture, int src_width, int src_height) = 0; int src_texture, int src_width, int src_height, int layer = 0) = 0;
virtual void ApplyShader() = 0; virtual void ApplyShader() = 0;
protected: protected:

View File

@ -240,6 +240,42 @@ bool Renderer::CalculateTargetSize(unsigned int framebuffer_width, unsigned int
return false; return false;
} }
void Renderer::ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc, TargetRectangle& rightRc)
{
// Resize target to half its original size
TargetRectangle drawRc = rc;
if (g_ActiveConfig.iStereoMode == STEREO_TAB)
{
// The height may be negative due to flipped rectangles
int height = rc.bottom - rc.top;
drawRc.top += height / 4;
drawRc.bottom -= height / 4;
}
else
{
int width = rc.right - rc.left;
drawRc.left += width / 4;
drawRc.right -= width / 4;
}
// Create two target rectangle offset to the sides of the backbuffer
leftRc = drawRc, rightRc = drawRc;
if (g_ActiveConfig.iStereoMode == STEREO_TAB)
{
leftRc.top -= s_backbuffer_height / 4;
leftRc.bottom -= s_backbuffer_height / 4;
rightRc.top += s_backbuffer_height / 4;
rightRc.bottom += s_backbuffer_height / 4;
}
else
{
leftRc.left -= s_backbuffer_width / 4;
leftRc.right -= s_backbuffer_width / 4;
rightRc.left += s_backbuffer_width / 4;
rightRc.right += s_backbuffer_width / 4;
}
}
void Renderer::SetScreenshot(const std::string& filename) void Renderer::SetScreenshot(const std::string& filename)
{ {
std::lock_guard<std::mutex> lk(s_criticalScreenshot); std::lock_guard<std::mutex> lk(s_criticalScreenshot);

View File

@ -83,6 +83,8 @@ public:
static const TargetRectangle& GetTargetRectangle() { return target_rc; } static const TargetRectangle& GetTargetRectangle() { return target_rc; }
static void UpdateDrawRectangle(int backbuffer_width, int backbuffer_height); static void UpdateDrawRectangle(int backbuffer_width, int backbuffer_height);
// Use this to convert a single target rectangle to two stereo rectangles
static void ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc, TargetRectangle& rightRc);
// Use this to upscale native EFB coordinates to IDEAL internal resolution // Use this to upscale native EFB coordinates to IDEAL internal resolution
static int EFBToScaledX(int x); static int EFBToScaledX(int x);

View File

@ -239,3 +239,16 @@ private:
#define I_NORMALMATRICES "cnmtx" #define I_NORMALMATRICES "cnmtx"
#define I_POSTTRANSFORMMATRICES "cpostmtx" #define I_POSTTRANSFORMMATRICES "cpostmtx"
#define I_DEPTHPARAMS "cDepth" // farZ, zRange #define I_DEPTHPARAMS "cDepth" // farZ, zRange
#define I_STEREOPARAMS "cstereo"
static const char s_shader_uniforms[] =
"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
"\tfloat4 " I_PROJECTION"[4];\n"
"\tint4 " I_MATERIALS"[4];\n"
"\tLight " I_LIGHTS"[8];\n"
"\tfloat4 " I_TEXMATRICES"[24];\n"
"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
"\tfloat4 " I_NORMALMATRICES"[32];\n"
"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
"\tfloat4 " I_DEPTHPARAMS";\n"
"\tfloat4 " I_STEREOPARAMS";\n";

View File

@ -115,6 +115,13 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
{ {
g_texture_cache->ClearRenderTargets(); g_texture_cache->ClearRenderTargets();
} }
if ((config.iStereoMode > 0) != backup_config.s_stereo_3d ||
config.bStereoMonoEFBDepth != backup_config.s_mono_efb_depth)
{
g_texture_cache->DeleteShaders();
g_texture_cache->CompileShaders();
}
} }
backup_config.s_colorsamples = config.iSafeTextureCache_ColorSamples; backup_config.s_colorsamples = config.iSafeTextureCache_ColorSamples;
@ -126,6 +133,8 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
backup_config.s_texfmt_overlay_center = config.bTexFmtOverlayCenter; backup_config.s_texfmt_overlay_center = config.bTexFmtOverlayCenter;
backup_config.s_hires_textures = config.bHiresTextures; backup_config.s_hires_textures = config.bHiresTextures;
backup_config.s_copy_cache_enable = config.bEFBCopyCacheEnable; backup_config.s_copy_cache_enable = config.bEFBCopyCacheEnable;
backup_config.s_stereo_3d = config.iStereoMode > 0;
backup_config.s_mono_efb_depth = config.bStereoMonoEFBDepth;
} }
void TextureCache::Cleanup() void TextureCache::Cleanup()
@ -444,14 +453,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
// //
// TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies?
// TODO: Actually, it should be enough if the internal texture format matches... // TODO: Actually, it should be enough if the internal texture format matches...
if ((entry->type == TCET_NORMAL && if (((entry->type == TCET_NORMAL &&
width == entry->virtual_width && width == entry->virtual_width &&
height == entry->virtual_height && height == entry->virtual_height &&
full_format == entry->format && full_format == entry->format &&
entry->num_mipmaps > maxlevel) || entry->num_mipmaps > maxlevel) ||
(entry->type == TCET_EC_DYNAMIC && (entry->type == TCET_EC_DYNAMIC &&
entry->native_width == width && entry->native_width == width &&
entry->native_height == height)) entry->native_height == height)) &&
entry->num_layers == 1)
{ {
// reuse the texture // reuse the texture
} }
@ -519,6 +529,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
// But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after. // But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after.
// Currently, we might try to reuse a texture which appears to have more levels than actual, maybe.. // Currently, we might try to reuse a texture which appears to have more levels than actual, maybe..
entry->num_mipmaps = maxlevel + 1; entry->num_mipmaps = maxlevel + 1;
entry->num_layers = 1;
entry->type = TCET_NORMAL; entry->type = TCET_NORMAL;
GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true);
@ -529,7 +540,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
entry->Load(width, height, expandedWidth, 0); entry->Load(width, height, expandedWidth, 0);
} }
entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps, entry->num_layers);
entry->SetDimensions(nativeW, nativeH, width, height); entry->SetDimensions(nativeW, nativeH, width, height);
entry->hash = tex_hash; entry->hash = tex_hash;
@ -886,12 +897,12 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
TCacheEntryBase *entry = textures[dstAddr]; TCacheEntryBase *entry = textures[dstAddr];
if (entry) if (entry)
{ {
if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h) if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h && entry->num_layers == FramebufferManagerBase::GetEFBLayers())
{ {
scaled_tex_w = tex_w; scaled_tex_w = tex_w;
scaled_tex_h = tex_h; scaled_tex_h = tex_h;
} }
else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h)) else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h && entry->num_layers == FramebufferManagerBase::GetEFBLayers()))
{ {
if (entry->type == TCET_EC_VRAM) if (entry->type == TCET_EC_VRAM)
{ {
@ -914,7 +925,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
textures[dstAddr] = entry = AllocateRenderTarget(scaled_tex_w, scaled_tex_h); textures[dstAddr] = entry = AllocateRenderTarget(scaled_tex_w, scaled_tex_h);
// TODO: Using the wrong dstFormat, dumb... // TODO: Using the wrong dstFormat, dumb...
entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1); entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1, FramebufferManagerBase::GetEFBLayers());
entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h);
entry->SetHashes(TEXHASH_INVALID); entry->SetHashes(TEXHASH_INVALID);
entry->type = TCET_EC_VRAM; entry->type = TCET_EC_VRAM;

View File

@ -39,6 +39,7 @@ public:
enum TexCacheEntryType type; enum TexCacheEntryType type;
unsigned int num_mipmaps; unsigned int num_mipmaps;
unsigned int num_layers;
unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view
unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies
@ -46,12 +47,13 @@ public:
int frameCount; int frameCount;
void SetGeneralParameters(u32 _addr, u32 _size, u32 _format, unsigned int _num_mipmaps) void SetGeneralParameters(u32 _addr, u32 _size, u32 _format, unsigned int _num_mipmaps, unsigned int _num_layers)
{ {
addr = _addr; addr = _addr;
size_in_bytes = _size; size_in_bytes = _size;
format = _format; format = _format;
num_mipmaps = _num_mipmaps; num_mipmaps = _num_mipmaps;
num_layers = _num_layers;
} }
void SetDimensions(unsigned int _native_width, unsigned int _native_height, unsigned int _virtual_width, unsigned int _virtual_height) void SetDimensions(unsigned int _native_width, unsigned int _native_height, unsigned int _virtual_width, unsigned int _virtual_height)
@ -101,6 +103,9 @@ public:
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0; unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0;
virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0; virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0;
virtual void CompileShaders() = 0; // currently only implemented by OGL
virtual void DeleteShaders() = 0; // currently only implemented by OGL
static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height,
int format, unsigned int tlutaddr, int tlutfmt, bool use_mipmaps, unsigned int maxlevel, bool from_tmem); int format, unsigned int tlutaddr, int tlutfmt, bool use_mipmaps, unsigned int maxlevel, bool from_tmem);
static void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, PEControl::PixelFormat srcFormat, static void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, PEControl::PixelFormat srcFormat,
@ -140,6 +145,8 @@ private:
bool s_texfmt_overlay_center; bool s_texfmt_overlay_center;
bool s_hires_textures; bool s_hires_textures;
bool s_copy_cache_enable; bool s_copy_cache_enable;
bool s_stereo_3d;
bool s_mono_efb_depth;
} backup_config; } backup_config;
}; };

View File

@ -70,7 +70,7 @@ static void WriteSwizzler(char*& p, u32 format, API_TYPE ApiType)
if (ApiType == API_OPENGL) if (ApiType == API_OPENGL)
{ {
WRITE(p, "#define samp0 samp9\n"); WRITE(p, "#define samp0 samp9\n");
WRITE(p, "SAMPLER_BINDING(9) uniform sampler2D samp0;\n"); WRITE(p, "SAMPLER_BINDING(9) uniform sampler2DArray samp0;\n");
WRITE(p, " out vec4 ocol0;\n"); WRITE(p, " out vec4 ocol0;\n");
WRITE(p, "void main()\n"); WRITE(p, "void main()\n");
@ -120,7 +120,7 @@ static void WriteSwizzler(char*& p, u32 format, API_TYPE ApiType)
static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, int xoffset, API_TYPE ApiType) static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, int xoffset, API_TYPE ApiType)
{ {
WRITE(p, " %s = texture(samp0, uv0 + float2(%d, 0) * sample_offset).%s;\n", WRITE(p, " %s = texture(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0)).%s;\n",
dest, xoffset, colorComp dest, xoffset, colorComp
); );
} }

View File

@ -89,17 +89,8 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : ""); out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : "");
else else
out.Write("cbuffer VSBlock {\n"); out.Write("cbuffer VSBlock {\n");
out.Write( out.Write(s_shader_uniforms);
"\tfloat4 " I_POSNORMALMATRIX"[6];\n" out.Write("};\n");
"\tfloat4 " I_PROJECTION"[4];\n"
"\tint4 " I_MATERIALS"[4];\n"
"\tLight " I_LIGHTS"[8];\n"
"\tfloat4 " I_TEXMATRICES"[24];\n"
"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
"\tfloat4 " I_NORMALMATRICES"[32];\n"
"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
"\tfloat4 " I_DEPTHPARAMS";\n"
"};\n");
GenerateVSOutputStruct(out, api_type); GenerateVSOutputStruct(out, api_type);
@ -131,22 +122,33 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i); out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i);
} }
// Let's set up attributes uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
for (size_t i = 0; i < 8; ++i) if (g_ActiveConfig.iStereoMode > 0)
{ {
if (i < xfmem.numTexGen.numTexGens) out.Write("centroid out VS_OUTPUT o;\n");
{
out.Write("centroid out float3 uv%d;\n", i);
}
} }
out.Write("centroid out float4 clipPos;\n"); else
if (g_ActiveConfig.bEnablePixelLighting) {
out.Write("centroid out float4 Normal;\n"); // Let's set up attributes
for (size_t i = 0; i < 8; ++i)
{
if (i < xfmem.numTexGen.numTexGens)
{
out.Write("centroid out float3 uv%d;\n", i);
}
}
out.Write("centroid out float4 colors_02;\n"); out.Write("centroid out float4 clipPos;\n");
out.Write("centroid out float4 colors_12;\n"); if (g_ActiveConfig.bEnablePixelLighting)
out.Write("centroid out float4 Normal;\n");
out.Write("centroid out float4 colors_02;\n");
out.Write("centroid out float4 colors_12;\n");
}
out.Write("void main()\n{\n"); out.Write("void main()\n{\n");
if (g_ActiveConfig.iStereoMode <= 0)
out.Write("VS_OUTPUT o;\n");
} }
else // D3D else // D3D
{ {
@ -172,8 +174,9 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
if (components & VB_HAS_POSMTXIDX) if (components & VB_HAS_POSMTXIDX)
out.Write(" int posmtx : BLENDINDICES,\n"); out.Write(" int posmtx : BLENDINDICES,\n");
out.Write(" float4 rawpos : POSITION) {\n"); out.Write(" float4 rawpos : POSITION) {\n");
out.Write("VS_OUTPUT o;\n");
} }
out.Write("VS_OUTPUT o;\n");
// transforms // transforms
if (components & VB_HAS_POSMTXIDX) if (components & VB_HAS_POSMTXIDX)
@ -431,27 +434,32 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
if (api_type == API_OPENGL) if (api_type == API_OPENGL)
{ {
// Bit ugly here if (g_ActiveConfig.iStereoMode <= 0)
// TODO: Make pretty {
// Will look better when we bind uniforms in GLSL 1.3 // Bit ugly here
// clipPos/w needs to be done in pixel shader, not here // TODO: Make pretty
// Will look better when we bind uniforms in GLSL 1.3
// clipPos/w needs to be done in pixel shader, not here
for (unsigned int i = 0; i < xfmem.numTexGen.numTexGens; ++i) for (unsigned int i = 0; i < xfmem.numTexGen.numTexGens; ++i)
out.Write(" uv%d.xyz = o.tex%d;\n", i, i); out.Write("uv%d.xyz = o.tex%d;\n", i, i);
out.Write(" clipPos = o.clipPos;\n");
if (g_ActiveConfig.bEnablePixelLighting) out.Write("clipPos = o.clipPos;\n");
out.Write(" Normal = o.Normal;\n");
if (g_ActiveConfig.bEnablePixelLighting)
out.Write("Normal = o.Normal;\n");
out.Write("colors_02 = o.colors_0;\n");
out.Write("colors_12 = o.colors_1;\n");
}
out.Write("colors_02 = o.colors_0;\n");
out.Write("colors_12 = o.colors_1;\n");
out.Write("gl_Position = o.pos;\n"); out.Write("gl_Position = o.pos;\n");
out.Write("}\n");
} }
else // D3D else // D3D
{ {
out.Write("return o;\n}\n"); out.Write("return o;\n");
} }
out.Write("}\n");
if (is_writing_shadercode) if (is_writing_shadercode)
{ {
@ -475,7 +483,12 @@ void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE
GenerateVertexShader<VertexShaderCode>(object, components, api_type); GenerateVertexShader<VertexShaderCode>(object, components, api_type);
} }
void GenerateVSOutputStructForGS(ShaderCode& object, API_TYPE api_type) void GenerateVSOutputStruct(ShaderCode& object, API_TYPE api_type)
{ {
GenerateVSOutputStruct<ShaderCode>(object, api_type); GenerateVSOutputStruct<ShaderCode>(object, api_type);
} }
void GenerateVSOutputStruct(ShaderGeneratorInterface& object, API_TYPE api_type)
{
// Ignore unknown types
}

View File

@ -38,7 +38,7 @@ struct vertex_shader_uid_data
u32 numColorChans : 2; u32 numColorChans : 2;
u32 dualTexTrans_enabled : 1; u32 dualTexTrans_enabled : 1;
u32 pixel_lighting : 1; u32 pixel_lighting : 1;
u32 pad0 : 1; u32 stereo : 1;
u32 texMtxInfo_n_projection : 16; // Stored separately to guarantee that the texMtxInfo struct is 8 bits wide u32 texMtxInfo_n_projection : 16; // Stored separately to guarantee that the texMtxInfo struct is 8 bits wide
struct { struct {
@ -64,4 +64,5 @@ typedef ShaderCode VertexShaderCode; // TODO: Obsolete..
void GetVertexShaderUid(VertexShaderUid& object, u32 components, API_TYPE api_type); void GetVertexShaderUid(VertexShaderUid& object, u32 components, API_TYPE api_type);
void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE api_type); void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE api_type);
void GenerateVSOutputStructForGS(ShaderCode& object, API_TYPE api_type); void GenerateVSOutputStruct(ShaderCode& object, API_TYPE api_type);
void GenerateVSOutputStruct(ShaderGeneratorInterface& object, API_TYPE api_type);

View File

@ -489,7 +489,7 @@ void VertexShaderManager::SetConstants()
PRIM_LOG("Projection: %f %f %f %f %f %f\n", rawProjection[0], rawProjection[1], rawProjection[2], rawProjection[3], rawProjection[4], rawProjection[5]); PRIM_LOG("Projection: %f %f %f %f %f %f\n", rawProjection[0], rawProjection[1], rawProjection[2], rawProjection[3], rawProjection[4], rawProjection[5]);
if ((g_ActiveConfig.bFreeLook || g_ActiveConfig.bAnaglyphStereo ) && xfmem.projection.type == GX_PERSPECTIVE) if (g_ActiveConfig.bFreeLook && xfmem.projection.type == GX_PERSPECTIVE)
{ {
Matrix44 mtxA; Matrix44 mtxA;
Matrix44 mtxB; Matrix44 mtxB;
@ -512,6 +512,19 @@ void VertexShaderManager::SetConstants()
Matrix44::Multiply(s_viewportCorrection, projMtx, correctedMtx); Matrix44::Multiply(s_viewportCorrection, projMtx, correctedMtx);
memcpy(constants.projection, correctedMtx.data, 4*16); memcpy(constants.projection, correctedMtx.data, 4*16);
} }
if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
{
float offset = (g_ActiveConfig.iStereoSeparation / 1000.0f) * (g_ActiveConfig.iStereoSeparationPercent / 100.0f);
constants.stereoparams[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
constants.stereoparams[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
constants.stereoparams[2] = (g_ActiveConfig.iStereoConvergence / 10.0f) * (g_ActiveConfig.iStereoConvergencePercent / 100.0f);
}
else
{
constants.stereoparams[0] = constants.stereoparams[1] = 0;
}
dirty = true; dirty = true;
} }
} }

View File

@ -60,6 +60,7 @@
<ClCompile Include="PostProcessing.cpp" /> <ClCompile Include="PostProcessing.cpp" />
<ClCompile Include="RenderBase.cpp" /> <ClCompile Include="RenderBase.cpp" />
<ClCompile Include="Statistics.cpp" /> <ClCompile Include="Statistics.cpp" />
<ClCompile Include="GeometryShaderGen.cpp" />
<ClCompile Include="TextureCacheBase.cpp" /> <ClCompile Include="TextureCacheBase.cpp" />
<ClCompile Include="TextureConversionShader.cpp" /> <ClCompile Include="TextureConversionShader.cpp" />
<ClCompile Include="VertexLoader.cpp" /> <ClCompile Include="VertexLoader.cpp" />
@ -110,6 +111,7 @@
<ClInclude Include="RenderBase.h" /> <ClInclude Include="RenderBase.h" />
<ClInclude Include="ShaderGenCommon.h" /> <ClInclude Include="ShaderGenCommon.h" />
<ClInclude Include="Statistics.h" /> <ClInclude Include="Statistics.h" />
<ClInclude Include="GeometryShaderGen.h" />
<ClInclude Include="TextureCacheBase.h" /> <ClInclude Include="TextureCacheBase.h" />
<ClInclude Include="TextureConversionShader.h" /> <ClInclude Include="TextureConversionShader.h" />
<ClInclude Include="TextureDecoder.h" /> <ClInclude Include="TextureDecoder.h" />

View File

@ -143,6 +143,9 @@
<ClCompile Include="BoundingBox.cpp"> <ClCompile Include="BoundingBox.cpp">
<Filter>Util</Filter> <Filter>Util</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GeometryShaderGen.cpp">
<Filter>Shader Generators</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="CommandProcessor.h" /> <ClInclude Include="CommandProcessor.h" />
@ -275,6 +278,9 @@
<ClInclude Include="BoundingBox.h"> <ClInclude Include="BoundingBox.h">
<Filter>Util</Filter> <Filter>Util</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GeometryShaderGen.h">
<Filter>Shader Generators</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="CMakeLists.txt" /> <Text Include="CMakeLists.txt" />

View File

@ -38,6 +38,11 @@ VideoConfig::VideoConfig()
backend_info.APIType = API_NONE; backend_info.APIType = API_NONE;
backend_info.bUseMinimalMipCount = false; backend_info.bUseMinimalMipCount = false;
backend_info.bSupportsExclusiveFullscreen = false; backend_info.bSupportsExclusiveFullscreen = false;
// Game-specific stereoscopy settings
bStereoMonoEFBDepth = false;
iStereoSeparationPercent = 100;
iStereoConvergencePercent = 100;
} }
void VideoConfig::Load(const std::string& ini_file) void VideoConfig::Load(const std::string& ini_file)
@ -66,9 +71,6 @@ void VideoConfig::Load(const std::string& ini_file)
settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0); settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
settings->Get("FreeLook", &bFreeLook, 0); settings->Get("FreeLook", &bFreeLook, 0);
settings->Get("UseFFV1", &bUseFFV1, 0); settings->Get("UseFFV1", &bUseFFV1, 0);
settings->Get("AnaglyphStereo", &bAnaglyphStereo, false);
settings->Get("AnaglyphStereoSeparation", &iAnaglyphStereoSeparation, 200);
settings->Get("AnaglyphFocalAngle", &iAnaglyphFocalAngle, 0);
settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0); settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
settings->Get("FastDepthCalc", &bFastDepthCalc, true); settings->Get("FastDepthCalc", &bFastDepthCalc, true);
settings->Get("MSAA", &iMultisampleMode, 0); settings->Get("MSAA", &iMultisampleMode, 0);
@ -85,6 +87,10 @@ void VideoConfig::Load(const std::string& ini_file)
enhancements->Get("ForceFiltering", &bForceFiltering, 0); enhancements->Get("ForceFiltering", &bForceFiltering, 0);
enhancements->Get("MaxAnisotropy", &iMaxAnisotropy, 0); // NOTE - this is x in (1 << x) enhancements->Get("MaxAnisotropy", &iMaxAnisotropy, 0); // NOTE - this is x in (1 << x)
enhancements->Get("PostProcessingShader", &sPostProcessingShader, ""); enhancements->Get("PostProcessingShader", &sPostProcessingShader, "");
enhancements->Get("StereoMode", &iStereoMode, 0);
enhancements->Get("StereoSeparation", &iStereoSeparation, 20);
enhancements->Get("StereoConvergence", &iStereoConvergence, 20);
enhancements->Get("StereoSwapEyes", &bStereoSwapEyes, false);
IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks"); IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
hacks->Get("EFBAccessEnable", &bEFBAccessEnable, true); hacks->Get("EFBAccessEnable", &bEFBAccessEnable, true);
@ -140,9 +146,6 @@ void VideoConfig::GameIniLoad()
CHECK_SETTING("Video_Settings", "UseRealXFB", bUseRealXFB); CHECK_SETTING("Video_Settings", "UseRealXFB", bUseRealXFB);
CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples); CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples);
CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures); CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures);
CHECK_SETTING("Video_Settings", "AnaglyphStereo", bAnaglyphStereo);
CHECK_SETTING("Video_Settings", "AnaglyphStereoSeparation", iAnaglyphStereoSeparation);
CHECK_SETTING("Video_Settings", "AnaglyphFocalAngle", iAnaglyphFocalAngle);
CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting); CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc); CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode); CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
@ -179,6 +182,14 @@ void VideoConfig::GameIniLoad()
CHECK_SETTING("Video_Enhancements", "ForceFiltering", bForceFiltering); CHECK_SETTING("Video_Enhancements", "ForceFiltering", bForceFiltering);
CHECK_SETTING("Video_Enhancements", "MaxAnisotropy", iMaxAnisotropy); // NOTE - this is x in (1 << x) CHECK_SETTING("Video_Enhancements", "MaxAnisotropy", iMaxAnisotropy); // NOTE - this is x in (1 << x)
CHECK_SETTING("Video_Enhancements", "PostProcessingShader", sPostProcessingShader); CHECK_SETTING("Video_Enhancements", "PostProcessingShader", sPostProcessingShader);
CHECK_SETTING("Video_Enhancements", "StereoMode", iStereoMode);
CHECK_SETTING("Video_Enhancements", "StereoSeparation", iStereoSeparation);
CHECK_SETTING("Video_Enhancements", "StereoConvergence", iStereoConvergence);
CHECK_SETTING("Video_Enhancements", "StereoSwapEyes", bStereoSwapEyes);
CHECK_SETTING("Video_Stereoscopy", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
CHECK_SETTING("Video_Stereoscopy", "StereoSeparationPercent", iStereoSeparationPercent);
CHECK_SETTING("Video_Stereoscopy", "StereoConvergencePercent", iStereoConvergencePercent);
CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable); CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable);
CHECK_SETTING("Video_Hacks", "EFBCopyEnable", bEFBCopyEnable); CHECK_SETTING("Video_Hacks", "EFBCopyEnable", bEFBCopyEnable);
@ -203,6 +214,7 @@ void VideoConfig::VerifyValidity()
// TODO: Check iMaxAnisotropy value // TODO: Check iMaxAnisotropy value
if (iAdapter < 0 || iAdapter > ((int)backend_info.Adapters.size() - 1)) iAdapter = 0; if (iAdapter < 0 || iAdapter > ((int)backend_info.Adapters.size() - 1)) iAdapter = 0;
if (iMultisampleMode < 0 || iMultisampleMode >= (int)backend_info.AAModes.size()) iMultisampleMode = 0; if (iMultisampleMode < 0 || iMultisampleMode >= (int)backend_info.AAModes.size()) iMultisampleMode = 0;
if (!backend_info.bSupportsStereoscopy) iStereoMode = 0;
} }
void VideoConfig::Save(const std::string& ini_file) void VideoConfig::Save(const std::string& ini_file)
@ -230,9 +242,6 @@ void VideoConfig::Save(const std::string& ini_file)
settings->Set("DumpEFBTarget", bDumpEFBTarget); settings->Set("DumpEFBTarget", bDumpEFBTarget);
settings->Set("FreeLook", bFreeLook); settings->Set("FreeLook", bFreeLook);
settings->Set("UseFFV1", bUseFFV1); settings->Set("UseFFV1", bUseFFV1);
settings->Set("AnaglyphStereo", bAnaglyphStereo);
settings->Set("AnaglyphStereoSeparation", iAnaglyphStereoSeparation);
settings->Set("AnaglyphFocalAngle", iAnaglyphFocalAngle);
settings->Set("EnablePixelLighting", bEnablePixelLighting); settings->Set("EnablePixelLighting", bEnablePixelLighting);
settings->Set("FastDepthCalc", bFastDepthCalc); settings->Set("FastDepthCalc", bFastDepthCalc);
settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions); settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions);
@ -250,6 +259,10 @@ void VideoConfig::Save(const std::string& ini_file)
enhancements->Set("ForceFiltering", bForceFiltering); enhancements->Set("ForceFiltering", bForceFiltering);
enhancements->Set("MaxAnisotropy", iMaxAnisotropy); enhancements->Set("MaxAnisotropy", iMaxAnisotropy);
enhancements->Set("PostProcessingShader", sPostProcessingShader); enhancements->Set("PostProcessingShader", sPostProcessingShader);
enhancements->Set("StereoMode", iStereoMode);
enhancements->Set("StereoSeparation", iStereoSeparation);
enhancements->Set("StereoConvergence", iStereoConvergence);
enhancements->Set("StereoSwapEyes", bStereoSwapEyes);
IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks"); IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
hacks->Set("EFBAccessEnable", bEFBAccessEnable); hacks->Set("EFBAccessEnable", bEFBAccessEnable);

View File

@ -44,6 +44,14 @@ enum EFBScale
SCALE_4X, SCALE_4X,
}; };
enum StereoMode
{
STEREO_OFF = 0,
STEREO_SBS,
STEREO_TAB,
STEREO_ANAGLYPH
};
// NEVER inherit from this class. // NEVER inherit from this class.
struct VideoConfig final struct VideoConfig final
{ {
@ -71,6 +79,10 @@ struct VideoConfig final
bool bForceFiltering; bool bForceFiltering;
int iMaxAnisotropy; int iMaxAnisotropy;
std::string sPostProcessingShader; std::string sPostProcessingShader;
int iStereoMode;
int iStereoSeparation;
int iStereoConvergence;
bool bStereoSwapEyes;
// Information // Information
bool bShowFPS; bool bShowFPS;
@ -92,9 +104,6 @@ struct VideoConfig final
bool bDumpEFBTarget; bool bDumpEFBTarget;
bool bUseFFV1; bool bUseFFV1;
bool bFreeLook; bool bFreeLook;
bool bAnaglyphStereo;
int iAnaglyphStereoSeparation;
int iAnaglyphFocalAngle;
bool bBorderlessFullscreen; bool bBorderlessFullscreen;
// Hacks // Hacks
@ -115,6 +124,11 @@ struct VideoConfig final
int iLog; // CONF_ bits int iLog; // CONF_ bits
int iSaveTargetId; // TODO: Should be dropped int iSaveTargetId; // TODO: Should be dropped
// Stereoscopy
bool bStereoMonoEFBDepth;
int iStereoSeparationPercent;
int iStereoConvergencePercent;
// D3D only config, mostly to be merged into the above // D3D only config, mostly to be merged into the above
int iAdapter; int iAdapter;
@ -136,9 +150,11 @@ struct VideoConfig final
bool bSupportsDualSourceBlend; bool bSupportsDualSourceBlend;
bool bSupportsPrimitiveRestart; bool bSupportsPrimitiveRestart;
bool bSupportsOversizedViewports; bool bSupportsOversizedViewports;
bool bSupportsStereoscopy;
bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon
bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon
bool bSupportsBBox; bool bSupportsBBox;
bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon
} backend_info; } backend_info;
// Utility // Utility