Initial support for using frame buffer as texture

This commit is contained in:
gdkchan 2018-04-12 02:09:20 -03:00
parent 9227b0ea59
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);
Map.GpuAddress = Offset;
return 0;
}

View file

@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
public int Align;
public int Kind;
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);
Renderer.SetFrameBuffer(Map.GpuAddress);
long Address = Map.CpuAddress;
if (MapFb.HasBufferOffset(Slot))

View file

@ -39,11 +39,13 @@ namespace Ryujinx.Graphics.Gal
GalBlendFactor FuncDstAlpha);
//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
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
@ -72,6 +74,8 @@ namespace Ryujinx.Graphics.Gal
//Texture
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.Graphics.OpenGL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
@ -8,7 +9,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
private struct FrameBuffer
{
public int FbHandle;
public int Handle;
public int RbHandle;
public int TexHandle;
}
@ -20,83 +21,121 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int FpHandle;
}
private FrameBuffer[] Fbs;
private Dictionary<long, FrameBuffer> Fbs;
private FrameBuffer CurrentFb;
private ShaderProgram Shader;
private bool IsInitialized;
private int FbHandle;
private int VaoHandle;
private int VboHandle;
public OGLFrameBuffer()
{
Fbs = new FrameBuffer[16];
Fbs = new Dictionary<long, FrameBuffer>();
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;
}
Fbs[Index].FbHandle = GL.GenFramebuffer();
Fbs[Index].RbHandle = GL.GenRenderbuffer();
Fbs[Index].TexHandle = GL.GenTexture();
FrameBuffer Fb = new FrameBuffer();
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.TextureMagFilter, (int)TextureMagFilter.Linear);
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);
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);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
FbHandle = Fb.Handle;
}
}
public void Draw(int Index)
public void BindTexture(long Tag, int Index)
{
if (Fbs[Index].FbHandle == 0)
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{
return;
GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
}
}
EnsureInitialized();
public void Set(long Tag)
{
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{
CurrentFb = Fb;
}
}
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
public void Render()
{
if (CurrentFb.TexHandle != 0)
{
EnsureInitialized();
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
GL.BindVertexArray(VaoHandle);
GL.ActiveTexture(TextureUnit.Texture0);
GL.UseProgram(Shader.Handle);
GL.BindTexture(TextureTarget.Texture2D, CurrentFb.TexHandle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
GL.BindVertexArray(VaoHandle);
GL.UseProgram(Shader.Handle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
//Restore the original state.
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FbHandle);
GL.UseProgram(CurrentProgram);
}
}
private void EnsureInitialized()

View file

@ -15,9 +15,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
int Handle = EnsureTextureInitialized(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle);
Bind(Index);
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);
GL.BindTexture(TextureTarget.Texture2D, Handle);
}
public void Set(GalTextureSampler Sampler)
{
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);

View file

@ -63,7 +63,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render()
{
FbRenderer.Render();
//FbRenderer.Render();
FrameBuffer.Render();
}
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)
@ -239,14 +245,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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;
}
private ConstBuffer[] Cbs;
private ConstBuffer[] ConstBuffers;
private HashSet<long> FrameBuffers;
private bool HasDataToRender;
@ -48,7 +50,9 @@ namespace Ryujinx.Graphics.Gpu
AddMethod(0x8e4, 16, 1, CbData);
AddMethod(0x904, 1, 1, CbBind);
Cbs = new ConstBuffer[18];
ConstBuffers = new ConstBuffer[18];
FrameBuffers = new HashSet<long>();
}
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
@ -85,8 +89,6 @@ namespace Ryujinx.Graphics.Gpu
if (HasDataToRender)
{
HasDataToRender = false;
Gpu.Renderer.DrawFrameBuffer(0);
}
int Arg0 = PBEntry.Arguments[0];
@ -99,16 +101,20 @@ namespace Ryujinx.Graphics.Gpu
SetFrameBuffer(0);
Gpu.Renderer.ClearBuffers(Layer, Flags);
//Gpu.Renderer.ClearBuffers(Layer, Flags);
}
private void SetFrameBuffer(int FbIndex)
{
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
Gpu.Renderer.SetFb(FbIndex, Width, Height);
Gpu.Renderer.BindFrameBuffer(FbIndex);
FrameBuffers.Add(Address);
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
Gpu.Renderer.CreateFrameBuffer(Address, Width, Height);
Gpu.Renderer.BindFrameBuffer(Address);
}
private long[] UploadShaders(AMemory Memory)
@ -205,11 +211,13 @@ namespace Ryujinx.Graphics.Gpu
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++)
{
@ -241,8 +249,23 @@ namespace Ryujinx.Graphics.Gpu
TicPosition += TicIndex * 0x20;
TscPosition += TscIndex * 0x20;
Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition));
Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
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.BindTexture(TexIndex);
}
Gpu.Renderer.SetSampler(TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
}
private void UploadUniforms(AMemory Memory)
@ -262,9 +285,9 @@ namespace Ryujinx.Graphics.Gpu
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)
{
@ -414,16 +437,16 @@ namespace Ryujinx.Graphics.Gpu
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
{
Cbs[Index].Position = Position;
Cbs[Index].Enabled = Enabled;
ConstBuffers[Index].Position = Position;
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)
{
long Position = Cbs[Cbuf].Position;
long Position = ConstBuffers[Cbuf].Position;
int Value = Memory.ReadInt32(Position + Offset);