mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 22:25:41 +12:00
[GPU] Bottom screen stub
This commit is contained in:
parent
d32de77671
commit
8e90b8a103
6 changed files with 101 additions and 25 deletions
|
@ -33,7 +33,10 @@ namespace VirtualAddrs {
|
|||
|
||||
NormalHeapStart = 0x08000000,
|
||||
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
|
||||
LinearHeapEndOld = 0x1C000000,
|
||||
|
||||
LinearHeapStartNew = 0x30000000,
|
||||
LinearHeapEndNew = 0x40000000,
|
||||
|
||||
// Start of TLS for first thread. Next thread's storage will be at TLSBase + 0x1000, and so on
|
||||
TLSBase = 0xFF400000,
|
||||
|
|
|
@ -57,6 +57,9 @@ class Renderer {
|
|||
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
||||
const std::array<u32, regNum>& regs;
|
||||
|
||||
OpenGL::Texture screenTexture;
|
||||
OpenGL::Framebuffer screenFramebuffer;
|
||||
|
||||
OpenGL::Framebuffer getColourFBO();
|
||||
OpenGL::Texture getTexture(Texture& tex);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// Are concerned. We could overload the == operator, but that implies full equality
|
||||
// Including equality of the allocated OpenGL resources, which we don't want
|
||||
// - A "valid" member that tells us whether the function is still valid or not
|
||||
// - A "location" member which tells us which location in 3DS memory this surface occupies
|
||||
template <typename SurfaceType, size_t capacity>
|
||||
class SurfaceCache {
|
||||
// Vanilla std::optional can't hold actual references
|
||||
|
@ -40,6 +41,15 @@ public:
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
OptionalRef findFromAddress(u32 address) {
|
||||
for (auto& e : buffer) {
|
||||
if (e.location == address && e.valid)
|
||||
return e;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Adds a surface object to the cache and returns it
|
||||
SurfaceType& add(const SurfaceType& surface) {
|
||||
if (size >= capacity) {
|
||||
|
|
|
@ -187,8 +187,8 @@ void Renderer::initGraphicsContext() {
|
|||
OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex);
|
||||
OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment);
|
||||
displayProgram.create({ vertDisplay, fragDisplay });
|
||||
displayProgram.use();
|
||||
|
||||
displayProgram.use();
|
||||
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
|
||||
|
||||
vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW);
|
||||
|
@ -208,18 +208,35 @@ void Renderer::initGraphicsContext() {
|
|||
|
||||
dummyVBO.create();
|
||||
dummyVAO.create();
|
||||
|
||||
// Create texture and framebuffer for the 3DS screen
|
||||
const u32 screenTextureWidth = 2 * 400; // Top screen is 400 pixels wide, bottom is 320
|
||||
const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall
|
||||
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
screenTexture.create(screenTextureWidth, screenTextureHeight, GL_RGBA8);
|
||||
screenTexture.bind();
|
||||
screenTexture.setMinFilter(OpenGL::Linear);
|
||||
screenTexture.setMagFilter(OpenGL::Linear);
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
|
||||
screenFramebuffer.createWithDrawTexture(screenTexture);
|
||||
screenFramebuffer.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
Helpers::panic("Incomplete framebuffer");
|
||||
|
||||
// TODO: This should not clear the framebuffer contents. It should load them from VRAM.
|
||||
GLint oldViewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, oldViewport);
|
||||
OpenGL::setViewport(screenTextureWidth, screenTextureHeight);
|
||||
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
OpenGL::clearColor();
|
||||
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void Renderer::getGraphicsContext() {
|
||||
OpenGL::disableScissor();
|
||||
OpenGL::setViewport(400, 240);
|
||||
|
||||
vbo.bind();
|
||||
vao.bind();
|
||||
triangleProgram.use();
|
||||
}
|
||||
|
||||
// Set up the OpenGL blending context to match the emulated PICA
|
||||
void Renderer::setupBlending() {
|
||||
const bool blendingEnabled = (regs[PICAInternalRegs::ColourOperation] & (1 << 8)) != 0;
|
||||
|
@ -266,6 +283,12 @@ void Renderer::setupBlending() {
|
|||
}
|
||||
|
||||
void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) {
|
||||
OpenGL::disableScissor();
|
||||
|
||||
vbo.bind();
|
||||
vao.bind();
|
||||
triangleProgram.use();
|
||||
|
||||
// Adjust alpha test if necessary
|
||||
const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
|
||||
if (alphaControl != oldAlphaControl) {
|
||||
|
@ -361,20 +384,11 @@ constexpr u32 bottomScreenBuffer = 0x1f05dc00;
|
|||
|
||||
// Quick hack to display top screen for now
|
||||
void Renderer::display() {
|
||||
OpenGL::disableBlend();
|
||||
OpenGL::disableDepth();
|
||||
OpenGL::disableScissor();
|
||||
|
||||
OpenGL::bindScreenFramebuffer();
|
||||
colourBufferCache[0].texture.bind();
|
||||
|
||||
displayProgram.use();
|
||||
|
||||
dummyVAO.bind();
|
||||
OpenGL::setClearColor(0.0, 0.0, 1.0, 1.0); // Clear screen colour
|
||||
OpenGL::clearColor();
|
||||
OpenGL::setViewport(0, 240, 400, 240); // Actually draw our 3DS screen
|
||||
OpenGL::draw(OpenGL::TriangleStrip, 4);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
|
||||
glBlitFramebuffer(0, 0, 400, 480, 0, 0, 400, 480, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
|
||||
void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
|
@ -408,6 +422,7 @@ OpenGL::Framebuffer Renderer::getColourFBO() {
|
|||
if (buffer.has_value()) {
|
||||
return buffer.value().get().fbo;
|
||||
} else {
|
||||
printf("New colour buffer: %08X\n", colourBufferLoc);
|
||||
return colourBufferCache.add(sampleBuffer).fbo;
|
||||
}
|
||||
}
|
||||
|
@ -450,4 +465,28 @@ void Renderer::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32
|
|||
|
||||
const u32 outputWidth = outputSize & 0xffff;
|
||||
const u32 outputGap = outputSize >> 16;
|
||||
|
||||
auto framebuffer = colourBufferCache.findFromAddress(inputAddr);
|
||||
if (framebuffer.has_value()) {
|
||||
OpenGL::Texture& texture = framebuffer.value().get().texture;
|
||||
texture.bind();
|
||||
screenFramebuffer.bind(OpenGL::DrawFramebuffer);
|
||||
|
||||
OpenGL::disableBlend();
|
||||
OpenGL::disableDepth();
|
||||
OpenGL::disableScissor();
|
||||
|
||||
displayProgram.use();
|
||||
|
||||
// Hack: Detect whether we are writing to the top or bottom screen by checking output gap and drawing to the proper part of the output texture
|
||||
// We consider output gap == 320 to mean bottom, and anything else to mean top
|
||||
if (outputGap == 320) {
|
||||
OpenGL::setViewport(40, 0, 320, 240);
|
||||
} else {
|
||||
OpenGL::setViewport(0, 240, 400, 240);
|
||||
}
|
||||
|
||||
dummyVAO.bind();
|
||||
OpenGL::draw(OpenGL::TriangleStrip, 4); // Actually draw our 3DS screen
|
||||
}
|
||||
}
|
|
@ -331,9 +331,31 @@ void GPUService::memoryFill(u32* cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
static u32 VaddrToPaddr(u32 addr) {
|
||||
if (addr >= VirtualAddrs::VramStart && addr < (VirtualAddrs::VramStart + VirtualAddrs::VramSize)) [[likely]] {
|
||||
return addr - VirtualAddrs::VramStart + PhysicalAddrs::VRAM;
|
||||
}
|
||||
|
||||
else if (addr >= VirtualAddrs::LinearHeapStartOld && addr < VirtualAddrs::LinearHeapEndOld) {
|
||||
return addr - VirtualAddrs::LinearHeapStartOld + PhysicalAddrs::FCRAM;
|
||||
}
|
||||
|
||||
else if (addr >= VirtualAddrs::LinearHeapStartNew && addr < VirtualAddrs::LinearHeapEndNew) {
|
||||
return addr - VirtualAddrs::LinearHeapStartNew + PhysicalAddrs::FCRAM;
|
||||
}
|
||||
|
||||
else if (addr == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Helpers::warn("[GSP::GPU VaddrToPaddr] Unknown virtual address %08X", addr);
|
||||
// Obviously garbage address
|
||||
return 0xF3310932;
|
||||
}
|
||||
|
||||
void GPUService::triggerDisplayTransfer(u32* cmd) {
|
||||
const u32 inputAddr = cmd[1];
|
||||
const u32 outputAddr = cmd[2];
|
||||
const u32 inputAddr = VaddrToPaddr(cmd[1]);
|
||||
const u32 outputAddr = VaddrToPaddr(cmd[2]);
|
||||
const u32 inputSize = cmd[3];
|
||||
const u32 outputSize = cmd[4];
|
||||
const u32 flags = cmd[5];
|
||||
|
|
|
@ -23,7 +23,6 @@ void Emulator::render() {
|
|||
|
||||
void Emulator::run() {
|
||||
while (running) {
|
||||
gpu.getGraphicsContext(); // Give the GPU a rendering context
|
||||
runFrame(); // Run 1 frame of instructions
|
||||
gpu.display(); // Display graphics
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue