Initial support for using frame buffer as texture

This commit is contained in:
gdkchan 2018-04-12 02:09:20 -03:00
commit f39ced70c1
8 changed files with 151 additions and 68 deletions

View file

@ -244,6 +244,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
Context.Memory.WriteInt64(Position + 0x20, Offset); Context.Memory.WriteInt64(Position + 0x20, Offset);
Map.GpuAddress = Offset;
return 0; return 0;
} }

View file

@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
public int Align; public int Align;
public int Kind; public int Kind;
public long CpuAddress; public long CpuAddress;
public long GpuAddress;
} }
} }

View file

@ -296,6 +296,8 @@ namespace Ryujinx.Core.OsHle.Services.Android
NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0); NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
Renderer.SetFrameBuffer(Map.GpuAddress);
long Address = Map.CpuAddress; long Address = Map.CpuAddress;
if (MapFb.HasBufferOffset(Slot)) if (MapFb.HasBufferOffset(Slot))

View file

@ -39,11 +39,13 @@ namespace Ryujinx.Graphics.Gal
GalBlendFactor FuncDstAlpha); GalBlendFactor FuncDstAlpha);
//Frame Buffer //Frame Buffer
void SetFb(int FbIndex, int Width, int Height); void CreateFrameBuffer(long Tag, int Width, int Height);
void BindFrameBuffer(int FbIndex); void BindFrameBuffer(long Tag);
void DrawFrameBuffer(int FbIndex); void BindFrameBufferTexture(long Tag, int Index);
void SetFrameBuffer(long Tag);
//Rasterizer //Rasterizer
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
@ -72,6 +74,8 @@ namespace Ryujinx.Graphics.Gal
//Texture //Texture
void SetTexture(int Index, GalTexture Tex); void SetTexture(int Index, GalTexture Tex);
void SetSampler(int Index, GalTextureSampler Sampler); void BindTexture(int Index);
void SetSampler(GalTextureSampler Sampler);
} }
} }

View file

@ -1,6 +1,7 @@
using OpenTK; using OpenTK;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System; using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
@ -8,7 +9,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
private struct FrameBuffer private struct FrameBuffer
{ {
public int FbHandle; public int Handle;
public int RbHandle; public int RbHandle;
public int TexHandle; public int TexHandle;
} }
@ -20,83 +21,121 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int FpHandle; public int FpHandle;
} }
private FrameBuffer[] Fbs; private Dictionary<long, FrameBuffer> Fbs;
private FrameBuffer CurrentFb;
private ShaderProgram Shader; private ShaderProgram Shader;
private bool IsInitialized; private bool IsInitialized;
private int FbHandle;
private int VaoHandle; private int VaoHandle;
private int VboHandle; private int VboHandle;
public OGLFrameBuffer() public OGLFrameBuffer()
{ {
Fbs = new FrameBuffer[16]; Fbs = new Dictionary<long, FrameBuffer>();
Shader = new ShaderProgram(); Shader = new ShaderProgram();
} }
public void Set(int Index, int Width, int Height) public void Create(long Tag, int Width, int Height)
{ {
if (Fbs[Index].FbHandle != 0) if (Fbs.ContainsKey(Tag))
{ {
return; return;
} }
Fbs[Index].FbHandle = GL.GenFramebuffer(); FrameBuffer Fb = new FrameBuffer();
Fbs[Index].RbHandle = GL.GenRenderbuffer();
Fbs[Index].TexHandle = GL.GenTexture();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); Fb.Handle = GL.GenFramebuffer();
Fb.RbHandle = GL.GenRenderbuffer();
Fb.TexHandle = GL.GenTexture();
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle); GL.RenderbufferStorage(
RenderbufferTarget.Renderbuffer,
RenderbufferStorage.Depth24Stencil8,
1280,
720);
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); GL.FramebufferRenderbuffer(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
RenderbufferTarget.Renderbuffer,
Fb.RbHandle);
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0); GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fb.TexHandle, 0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Fbs.Add(Tag, Fb);
} }
public void Bind(int Index) public void Bind(long Tag)
{ {
if (Fbs[Index].FbHandle == 0) if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{ {
return; GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
FbHandle = Fb.Handle;
}
} }
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); public void BindTexture(long Tag, int Index)
{
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
}
} }
public void Draw(int Index) public void Set(long Tag)
{ {
if (Fbs[Index].FbHandle == 0) if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{ {
return; CurrentFb = Fb;
}
} }
public void Render()
{
if (CurrentFb.TexHandle != 0)
{
EnsureInitialized(); EnsureInitialized();
int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
GL.ActiveTexture(TextureUnit.Texture0); GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, CurrentFb.TexHandle);
GL.BindVertexArray(VaoHandle); GL.BindVertexArray(VaoHandle);
GL.UseProgram(Shader.Handle); GL.UseProgram(Shader.Handle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
//Restore the original state.
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FbHandle);
GL.UseProgram(CurrentProgram);
}
} }
private void EnsureInitialized() private void EnsureInitialized()

View file

@ -15,9 +15,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.ActiveTexture(TextureUnit.Texture0 + Index);
int Handle = EnsureTextureInitialized(Index); Bind(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle);
const int Border = 0; const int Border = 0;
@ -54,12 +52,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public void Set(int Index, GalTextureSampler Sampler) public void Bind(int Index)
{ {
int Handle = EnsureTextureInitialized(Index); int Handle = EnsureTextureInitialized(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle); GL.BindTexture(TextureTarget.Texture2D, Handle);
}
public void Set(GalTextureSampler Sampler)
{
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU); int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV); int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);

View file

@ -63,7 +63,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render() public void Render()
{ {
FbRenderer.Render(); //FbRenderer.Render();
FrameBuffer.Render();
} }
public void SetWindowSize(int Width, int Height) public void SetWindowSize(int Width, int Height)
@ -132,19 +133,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}); });
} }
public void SetFb(int FbIndex, int Width, int Height) public void CreateFrameBuffer(long Tag, int Width, int Height)
{ {
ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height)); ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
} }
public void BindFrameBuffer(int FbIndex) public void BindFrameBuffer(long Tag)
{ {
ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex)); ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
} }
public void DrawFrameBuffer(int FbIndex) public void BindFrameBufferTexture(long Tag, int Index)
{ {
ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex)); ActionsQueue.Enqueue(() => FrameBuffer.BindTexture(Tag, Index));
}
public void SetFrameBuffer(long Tag)
{
ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag));
} }
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
@ -239,14 +245,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ActionsQueue.Enqueue(() => Shader.BindProgram()); ActionsQueue.Enqueue(() => Shader.BindProgram());
} }
public void SetTexture(int Index, GalTexture Tex) public void SetTexture(int Index, GalTexture Texture)
{ {
ActionsQueue.Enqueue(() => Texture.Set(Index, Tex)); ActionsQueue.Enqueue(() => this.Texture.Set(Index, Texture));
} }
public void SetSampler(int Index, GalTextureSampler Sampler) public void BindTexture(int Index)
{ {
ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler)); ActionsQueue.Enqueue(() => Texture.Bind(Index));
}
public void SetSampler(GalTextureSampler Sampler)
{
ActionsQueue.Enqueue(() => Texture.Set(Sampler));
} }
} }
} }

View file

@ -20,7 +20,9 @@ namespace Ryujinx.Graphics.Gpu
public int Size; public int Size;
} }
private ConstBuffer[] Cbs; private ConstBuffer[] ConstBuffers;
private HashSet<long> FrameBuffers;
private bool HasDataToRender; private bool HasDataToRender;
@ -48,7 +50,9 @@ namespace Ryujinx.Graphics.Gpu
AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x8e4, 16, 1, CbData);
AddMethod(0x904, 1, 1, CbBind); AddMethod(0x904, 1, 1, CbBind);
Cbs = new ConstBuffer[18]; ConstBuffers = new ConstBuffer[18];
FrameBuffers = new HashSet<long>();
} }
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
@ -85,8 +89,6 @@ namespace Ryujinx.Graphics.Gpu
if (HasDataToRender) if (HasDataToRender)
{ {
HasDataToRender = false; HasDataToRender = false;
Gpu.Renderer.DrawFrameBuffer(0);
} }
int Arg0 = PBEntry.Arguments[0]; int Arg0 = PBEntry.Arguments[0];
@ -99,16 +101,20 @@ namespace Ryujinx.Graphics.Gpu
SetFrameBuffer(0); SetFrameBuffer(0);
Gpu.Renderer.ClearBuffers(Layer, Flags); //Gpu.Renderer.ClearBuffers(Layer, Flags);
} }
private void SetFrameBuffer(int FbIndex) private void SetFrameBuffer(int FbIndex)
{ {
long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
FrameBuffers.Add(Address);
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
Gpu.Renderer.SetFb(FbIndex, Width, Height); Gpu.Renderer.CreateFrameBuffer(Address, Width, Height);
Gpu.Renderer.BindFrameBuffer(FbIndex); Gpu.Renderer.BindFrameBuffer(Address);
} }
private long[] UploadShaders(AMemory Memory) private long[] UploadShaders(AMemory Memory)
@ -205,11 +211,13 @@ namespace Ryujinx.Graphics.Gpu
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
long BasePosition = Cbs[TextureCbIndex].Position; long BasePosition = ConstBuffers[TextureCbIndex].Position;
long Size = (uint)Cbs[TextureCbIndex].Size; long Size = (uint)ConstBuffers[TextureCbIndex].Size;
int TexIndex = 0; //Note: On the emulator renderer, Texture Unit 0 is
//reserved for drawing the frame buffer.
int TexIndex = 1;
for (int Index = 0; Index < Tags.Length; Index++) for (int Index = 0; Index < Tags.Length; Index++)
{ {
@ -241,8 +249,23 @@ namespace Ryujinx.Graphics.Gpu
TicPosition += TicIndex * 0x20; TicPosition += TicIndex * 0x20;
TscPosition += TscIndex * 0x20; TscPosition += TscIndex * 0x20;
long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff;
if (FrameBuffers.Contains(TextureAddress))
{
//This texture is a frame buffer texture,
//we shouldn't read anything from memory and bind
//the frame buffer texture instead, since we're not
//really writing anything to memory.
Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex);
}
else
{
Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition)); Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition));
Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition)); Gpu.Renderer.BindTexture(TexIndex);
}
Gpu.Renderer.SetSampler(TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
} }
private void UploadUniforms(AMemory Memory) private void UploadUniforms(AMemory Memory)
@ -262,9 +285,9 @@ namespace Ryujinx.Graphics.Gpu
continue; continue;
} }
for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++) for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++)
{ {
ConstBuffer Cb = Cbs[Cbuf]; ConstBuffer Cb = ConstBuffers[Cbuf];
if (Cb.Enabled) if (Cb.Enabled)
{ {
@ -414,16 +437,16 @@ namespace Ryujinx.Graphics.Gpu
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
{ {
Cbs[Index].Position = Position; ConstBuffers[Index].Position = Position;
Cbs[Index].Enabled = Enabled; ConstBuffers[Index].Enabled = Enabled;
Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
} }
} }
private int ReadCb(AMemory Memory, int Cbuf, int Offset) private int ReadCb(AMemory Memory, int Cbuf, int Offset)
{ {
long Position = Cbs[Cbuf].Position; long Position = ConstBuffers[Cbuf].Position;
int Value = Memory.ReadInt32(Position + Offset); int Value = Memory.ReadInt32(Position + Offset);