Skip to content

Commit

Permalink
Merge pull request #1439 from Armada651/ogl-stereo-3d
Browse files Browse the repository at this point in the history
OGL: Stereoscopic 3D Support
  • Loading branch information
Sonicadvance1 committed Nov 28, 2014
2 parents 0e3d20c + 6d51455 commit ce05976
Show file tree
Hide file tree
Showing 48 changed files with 773 additions and 266 deletions.
2 changes: 2 additions & 0 deletions Data/Sys/GameSettings/GLME01.ini
Expand Up @@ -32,3 +32,5 @@ $99 of some treasures
040AE530 3F000063
$End Boss Has No HP

[Video_Stereoscopy]
StereoMonoEFBDepth = True
24 changes: 0 additions & 24 deletions Data/Sys/Shaders/stereoscopic.glsl

This file was deleted.

24 changes: 0 additions & 24 deletions Data/Sys/Shaders/stereoscopic2.glsl

This file was deleted.

7 changes: 7 additions & 0 deletions Source/Core/Common/MathUtil.cpp
Expand Up @@ -351,6 +351,13 @@ void Matrix44::Translate(Matrix44 &mtx, const float vec[3])
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)
{
MatrixMul(4, a.data, b.data, result.data);
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Common/MathUtil.h
Expand Up @@ -232,6 +232,7 @@ class Matrix44
static void Set(Matrix44 &mtx, const float mtxArray[16]);

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);

Expand Down
5 changes: 5 additions & 0 deletions Source/Core/Core/ConfigManager.cpp
Expand Up @@ -81,6 +81,11 @@ static const struct
{ "FreelookZoomOut", 83 /* 'S' */, 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 */ },
{ "LoadStateSlot2", 341 /* WXK_F2 */, 0 /* wxMOD_NONE */ },
{ "LoadStateSlot3", 342 /* WXK_F3 */, 0 /* wxMOD_NONE */ },
Expand Down
5 changes: 5 additions & 0 deletions Source/Core/Core/CoreParameter.h
Expand Up @@ -54,6 +54,11 @@ enum Hotkey
HK_FREELOOK_ZOOM_OUT,
HK_FREELOOK_RESET,

HK_INCREASE_SEPARATION,
HK_DECREASE_SEPARATION,
HK_INCREASE_CONVERGENCE,
HK_DECREASE_CONVERGENCE,

HK_LOAD_STATE_SLOT_1,
HK_LOAD_STATE_SLOT_2,
HK_LOAD_STATE_SLOT_3,
Expand Down
20 changes: 20 additions & 0 deletions Source/Core/DolphinWX/Frame.cpp
Expand Up @@ -1123,6 +1123,26 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
{
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
{
Expand Down
5 changes: 5 additions & 0 deletions Source/Core/DolphinWX/HotkeyDlg.cpp
Expand Up @@ -238,6 +238,11 @@ void HotkeyConfigDialog::CreateHotkeyGUIControls()
_("Freelook Zoom Out"),
_("Freelook Reset"),

_("Increase Stereocopy Separation"),
_("Decrease Stereocopy Separation"),
_("Increase Stereocopy Convergence"),
_("Decrease Stereocopy Convergence"),

_("Load State Slot 1"),
_("Load State Slot 2"),
_("Load State Slot 3"),
Expand Down
41 changes: 40 additions & 1 deletion Source/Core/DolphinWX/VideoConfigDiag.cpp
Expand Up @@ -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 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 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__)
Expand Down Expand Up @@ -396,7 +400,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
if (vconfig.backend_info.PPShaders.size())
{
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));
choice_ppshader->AppendString(_("(off)"));

Expand Down Expand Up @@ -425,6 +429,11 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
szr_pp->Add(button_config_pp);
szr_enh->Add(szr_pp);
}
else
{
choice_ppshader = nullptr;
button_config_pp = nullptr;
}

// Scaled copy, PL, Bilinear filter
szr_enh->Add(CreateCheckBox(page_enh, _("Scaled EFB Copy"), wxGetTranslation(scaled_efb_copy_desc), vconfig.bCopyEFBScaled));
Expand All @@ -438,6 +447,36 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 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();
CreateDescriptionArea(page_enh, szr_enh_main);
Expand Down
22 changes: 22 additions & 0 deletions Source/Core/DolphinWX/VideoConfigDiag.h
Expand Up @@ -165,6 +165,20 @@ class VideoConfigDiag : public wxDialog
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_Close(wxCloseEvent&);

Expand All @@ -184,6 +198,12 @@ class VideoConfigDiag : public wxDialog
virtual_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
if (Core::IsRunning())
{
Expand Down Expand Up @@ -248,6 +268,8 @@ class VideoConfigDiag : public wxDialog

wxCheckBox* progressive_scan_checkbox;

wxChoice* choice_ppshader;

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

Expand Down
2 changes: 1 addition & 1 deletion Source/Core/VideoBackends/D3D/LineGeometryShader.cpp
Expand Up @@ -172,7 +172,7 @@ bool LineGeometryShader::SetShader(u32 components, float lineWidth,
static char buffer[16384];
ShaderCode code;
code.SetBuffer(buffer);
GenerateVSOutputStructForGS(code, API_D3D);
GenerateVSOutputStruct(code, API_D3D);
code.Write("\n%s", LINE_GS_COMMON);

std::stringstream numTexCoordsStream;
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/VideoBackends/D3D/PointGeometryShader.cpp
Expand Up @@ -166,7 +166,7 @@ bool PointGeometryShader::SetShader(u32 components, float pointSize,
static char buffer[16384];
ShaderCode code;
code.SetBuffer(buffer);
GenerateVSOutputStructForGS(code, API_D3D);
GenerateVSOutputStruct(code, API_D3D);
code.Write("\n%s", POINT_GS_COMMON);

std::stringstream numTexCoordsStream;
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/VideoBackends/D3D/TextureCache.h
Expand Up @@ -43,6 +43,9 @@ class TextureCache : public ::TextureCache

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;};

void CompileShaders() override { }
void DeleteShaders() override { }
};

}
1 change: 1 addition & 0 deletions Source/Core/VideoBackends/D3D/main.cpp
Expand Up @@ -78,6 +78,7 @@ void InitBackendInfo()
g_Config.backend_info.bSupportsPrimitiveRestart = true;
g_Config.backend_info.bSupportsOversizedViewports = false;
g_Config.backend_info.bSupportsBBox = false; // TODO: not implemented
g_Config.backend_info.bSupportsStereoscopy = false; // TODO: not implemented

IDXGIFactory* factory;
IDXGIAdapter* ad;
Expand Down

16 comments on commit ce05976

@CarlKenner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good on you for adding stereoscopic 3D support.
This totally breaks Dolphin VR though.
I don't even know if the Oculus SDK will accept 3D textures. :-(

@cegli
Copy link

@cegli cegli commented on ce05976 Nov 29, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I was just trying to merge this into Dolphin VR as well... Looks like quite a challenge...

@degasus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CarlKenner Why 3d texture? Just add an additional step afterwards to merge this two images as you want and you'll get a Dolphin VR without any slowdowns compared to master.

@CarlKenner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cegli I think you should give up merging it for now. I merged everything from master up to this commit, and pulled RachelBryk's merge after this one into my branch. So just pull my branch for now.

@degasus I thought blitting from the 3D texture to two 2D ones would make it slower.

@degasus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CarlKenner Only a bit, likely not noticeable. This way to render still has (a bit more than) the double GPU effort, but as long as your GPU is fine (don't play at 4xIR in stereo on a consumer GPU...) there is no additional slowdown. The old DolphinVR implementation did switch the framebuffer twice on every draw call, this was a huge CPU overhead within the driver.

But indeed, this commit makes some of your 3d patches obsolete, but I think it's worth to port DolphinVR over to this technic. So it's basicly:

  • port your projection matrix hacks into the current geometry shaders (it must be switch-able within the shader to not split the draw calls)
  • write a postprocessing step which seperate this texture array into two textures. This is likely just two glBlitFramebuffer with some binding calls before
  • have fun playing lots of games fullspeed on a consumer CPU

@CarlKenner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'm not that stupid, I only switch the framebuffer once on every draw call. I realise that is still once too many, but at least I had the sense not to do it twice:

  • For each object
    • Draw object for current eye
    • Swap current eye
    • Draw object for current eye

I'll see if I can merge these changes, but it will take a while, and there are some other features I need to add first and test on code that I know works, before I can mess up the code with my poor attempt at merging.

This method has additional hardware and driver requirements though, is that right? What are the requirements and how common are they likely to be for users?

@degasus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea. Haven't thought about this way to save one framebuffer binding call :D

Yeah, I see porting your patches to this framework is lots of work. but I hope you'll succed on the task.

The new requirement is basicly geometry shader aka OpenGL 3.2. It should be supported by every GPU we support right now. Geometry shaders are a hard dependency for all DX10 GPUS which we already require for integer shaders. But bad luck, some drivers doesn't offer this:

  • intel sandybridge on windows ( the hardware is able to, but the driver just doesn't expose it)
  • some rarely used mesa drivers (most of them already support 3.3 for half a year)

@CarlKenner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hard part will be getting the telescope to work. Link holds the telescope up to his left eye only, while his right eye still sees the world unmagnified until he closes his right eye.

@JMC47
Copy link
Contributor

@JMC47 JMC47 commented on ce05976 Nov 29, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Armada will have an idea on how to handle that. I never really thought about that case until you just mentioned it. 3D support is more or less about making it 3D; where as the oculus rift and other VR units would have stuff like the situation with Link's Telescope. Seems like a scary difference...

@CrossVR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CarlKenner It's not really necessary to port these changes to DolphinVR, as long as you don't enable the stereoscopic 3D option you can still continue to use your own method of 3D rendering. I'm actually interested in measuring the performance difference between the two different methods.

The most difficult part in merging will not be layered textures, as degasus explained you can just blit these to standard 2D textures. What's more difficult is that the current geometry shader is not compatible with the projection matrices supplied by the Oculus SDK. You'll have to revert commit 6cacfad where compatibility with stereo projection matrices was removed from the geometry shader.

About the telescope, if you want to black out one eye when holding the telescope the best method would be to give the telescope texture two layers where the second layer is completely black. The pixel shader will automatically sample the correct layer and it would black out only the right eye. The texture cache can already replace textures by custom ones, all that needs to be added is support for custom textures to be multi-layered. Ofcourse, this should only be done in a VR headset, it would look very strange for a 3D monitor to be black for one eye.

@penkamaster
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Armada651 How I can enable 3d stereo rendering? I dont see the option

@cammelspit
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@degasus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@penkamaster Look in the video ini, there is an option for it. But don't expect it to be feature complete

@CarlKenner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've got it working in Dolphin VR. With the convergence at max, and the separation at about .4 it works well enough to play. The speed is pretty good too.
I just had to move glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); out of m_post_processor->BlitFromTexture so the same function can be used to blit to the 2D eye framebuffers. Hopefully that means post-processing shaders can actually be used in Dolphin VR now (they weren't implemented before).
Now I just need to separate the separation into two separate parameters, one for the shear and one for the camera movement, and in VR mode I need to set the three parameters programmatically to match the Oculus SDK, rather than using the ones the user sets (there's no such thing as separation and convergence in VR, because VR has to match exactly what your eyes would see in the real world).

@CrossVR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CarlKenner Have a look at commit 51a4d6a that's when the geometry shader still used a shearing parameter.

@CarlKenner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I found a bug, but I was wrong. Sorry.

Please sign in to comment.