void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH, int rotation) { float outW; float outH; bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180; if (g_Config.iSmallDisplayZoomType == 0) { // Stretching outW = frameW; outH = frameH; } else { if (g_Config.iSmallDisplayZoomType == 3) { // Manual Scaling float offsetX = (g_Config.fSmallDisplayOffsetX - 0.5f) * 2.0f * frameW; float offsetY = (g_Config.fSmallDisplayOffsetY - 0.5f) * 2.0f * frameH; // Have to invert Y for GL if (GetGPUBackend() == GPUBackend::OPENGL) { offsetY = offsetY * -1.0f; } float customZoom = g_Config.fSmallDisplayZoomLevel; float smallDisplayW = origW * customZoom; float smallDisplayH = origH * customZoom; if (!rotated) { *x = floorf(((frameW - smallDisplayW) / 2.0f) + offsetX); *y = floorf(((frameH - smallDisplayH) / 2.0f) + offsetY); *w = floorf(smallDisplayW); *h = floorf(smallDisplayH); return; } else { *x = floorf(((frameW - smallDisplayH) / 2.0f) + offsetX); *y = floorf(((frameH - smallDisplayW) / 2.0f) + offsetY); *w = floorf(smallDisplayH); *h = floorf(smallDisplayW); return; } } else if (g_Config.iSmallDisplayZoomType == 2) { // Auto Scaling float pixelCrop = frameH / 270.0f; float resCommonWidescreen = pixelCrop - floor(pixelCrop); if (!rotated && resCommonWidescreen == 0.0f) { *x = 0; *y = floorf(-pixelCrop); *w = floorf(frameW); *h = floorf(pixelCrop * 272.0f); return; } } float origRatio = !rotated ? origW / origH : origH / origW; float frameRatio = frameW / frameH; if (origRatio > frameRatio) { // Image is wider than frame. Center vertically. outW = frameW; outH = frameW / origRatio; // Stretch a little bit if (!rotated && g_Config.iSmallDisplayZoomType == 1) // Partial Stretch outH = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 } else { // Image is taller than frame. Center horizontally. outW = frameH * origRatio; outH = frameH; if (rotated && g_Config.iSmallDisplayZoomType == 1) // Partial Stretch outW = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 } } *x = floorf((frameW - outW) / 2.0f); *y = floorf((frameH - outH) / 2.0f); *w = floorf(outW); *h = floorf(outH); }
// Copies RGBA8 data from RAM to the currently bound render target. void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { float dstwidth = (float)PSP_CoreParameter().pixelWidth; float dstheight = (float)PSP_CoreParameter().pixelHeight; T3DViewport viewport = {0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f}; thin3d->SetViewports(1, &viewport); thin3d->SetBlendState(thin3d->GetBlendStatePreset(BS_OFF)); Thin3DSamplerState *sampler; if (g_Config.iBufFilter == SCALE_NEAREST) { sampler = thin3d->GetSamplerStatePreset(T3DSamplerStatePreset::SAMPS_NEAREST); } else { sampler = thin3d->GetSamplerStatePreset(T3DSamplerStatePreset::SAMPS_LINEAR); } thin3d->SetSamplerStates(0, 1, &sampler); thin3d->SetDepthStencilState(depth); thin3d->SetRenderState(T3DRenderState::CULL_MODE, T3DCullMode::NO_CULL); thin3d->SetScissorEnabled(false); float u0 = 0.0f; float u1; if (displayFramebuf_ == 0) { u8 data[] = {0, 0, 0, 0}; fbTex->SetImageData(0, 0, 0, 1, 1, 1, 0, 4, data); u1 = 1.0f; } else if (displayFormat_ == GE_FORMAT_8888) { u8 *data = Memory::GetPointer(displayFramebuf_); fbTex->SetImageData(0, 0, 0, displayStride_, srcheight, 1, 0, displayStride_ * 4, data); u1 = (float)srcwidth / displayStride_; } else { // TODO: This should probably be converted in a shader instead.. fbTexBuffer.resize(srcwidth * srcheight); FormatBuffer displayBuffer; displayBuffer.data = Memory::GetPointer(displayFramebuf_); for (int y = 0; y < srcheight; ++y) { u32 *buf_line = &fbTexBuffer[y * srcwidth]; const u16 *fb_line = &displayBuffer.as16[y * displayStride_]; switch (displayFormat_) { case GE_FORMAT_565: ConvertRGBA565ToRGBA8888(buf_line, fb_line, srcwidth); break; case GE_FORMAT_5551: ConvertRGBA5551ToRGBA8888(buf_line, fb_line, srcwidth); break; case GE_FORMAT_4444: ConvertRGBA4444ToRGBA8888(buf_line, fb_line, srcwidth); break; default: ERROR_LOG_REPORT(G3D, "Software: Unexpected framebuffer format: %d", displayFormat_); } } fbTex->SetImageData(0, 0, 0, srcwidth, srcheight, 1, 0, srcwidth * 4, (const uint8_t *)&fbTexBuffer[0]); u1 = 1.0f; } fbTex->Finalize(0); float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL); if (GetGPUBackend() == GPUBackend::DIRECT3D9) { x += 0.5f; y += 0.5f; } x /= 0.5f * dstwidth; y /= 0.5f * dstheight; w /= 0.5f * dstwidth; h /= 0.5f * dstheight; float x2 = x + w; float y2 = y + h; x -= 1.0f; y -= 1.0f; x2 -= 1.0f; y2 -= 1.0f; struct Vertex { float x, y, z; float u, v; uint32_t rgba; }; float v0 = 1.0f; float v1 = 0.0f; if (GetGPUBackend() == GPUBackend::VULKAN) { std::swap(v0, v1); } const Vertex verts[4] = { {x, y, 0, u0, v0, 0xFFFFFFFF}, // TL {x, y2, 0, u0, v1, 0xFFFFFFFF}, // BL {x2, y2, 0, u1, v1, 0xFFFFFFFF}, // BR {x2, y, 0, u1, v0, 0xFFFFFFFF}, // TR }; vdata->SetData((const uint8_t *)verts, sizeof(verts)); int indexes[] = {0, 1, 2, 0, 2, 3}; idata->SetData((const uint8_t *)indexes, sizeof(indexes)); thin3d->SetTexture(0, fbTex); Thin3DShaderSet *texColor = thin3d->GetShaderSetPreset(SS_TEXTURE_COLOR_2D); static const float identity4x4[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; texColor->SetMatrix4x4("WorldViewProj", identity4x4); thin3d->DrawIndexed(T3DPrimitive::PRIM_TRIANGLES, texColor, vformat, vdata, idata, 6, 0); }
void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH, int rotation) { float outW; float outH; bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180; if (g_Config.bStretchToDisplay) { outW = frameW; outH = frameH; } else { bool fullScreenZoom = true; #ifndef MOBILE_DEVICE // This would turn off small display in window mode. I think it's better to allow it. // fullScreenZoom = g_Config.bFullScreen; #endif if (fullScreenZoom) { if (g_Config.iSmallDisplayZoom != 0) { float offsetX = (g_Config.fSmallDisplayOffsetX - 0.5f) * 2.0f * frameW; float offsetY = (g_Config.fSmallDisplayOffsetY - 0.5f) * 2.0f * frameH; // Have to invert Y for GL if (GetGPUBackend() == GPUBackend::OPENGL) { offsetY = offsetY * -1.0f; } float customZoom = g_Config.fSmallDisplayCustomZoom / 8.0f; float smallDisplayW = origW * customZoom; float smallDisplayH = origH * customZoom; if (!rotated) { *x = floorf(((frameW - smallDisplayW) / 2.0f) + offsetX); *y = floorf(((frameH - smallDisplayH) / 2.0f) + offsetY); *w = floorf(smallDisplayW); *h = floorf(smallDisplayH); return; } else { *x = floorf(((frameW - smallDisplayH) / 2.0f) + offsetX); *y = floorf(((frameH - smallDisplayW) / 2.0f) + offsetY); *w = floorf(smallDisplayH); *h = floorf(smallDisplayW); return; } } else { float pixelCrop = frameH / 270.0f; float resCommonWidescreen = pixelCrop - floor(pixelCrop); if (!rotated && resCommonWidescreen == 0.0f) { *x = 0; *y = floorf(-pixelCrop); *w = floorf(frameW); *h = floorf(pixelCrop * 272.0f); return; } } } float origRatio = !rotated ? origW / origH : origH / origW; float frameRatio = frameW / frameH; if (origRatio > frameRatio) { // Image is wider than frame. Center vertically. outW = frameW; outH = frameW / origRatio; // Stretch a little bit if (!rotated && g_Config.bPartialStretch) outH = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 } else { // Image is taller than frame. Center horizontally. outW = frameH * origRatio; outH = frameH; } } *x = floorf((frameW - outW) / 2.0f); *y = floorf((frameH - outH) / 2.0f); *w = floorf(outW); *h = floorf(outH); }