From a0c0d6a97745b89934707faf850e71e75981d32d Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 1 Feb 2013 00:18:23 +0100 Subject: [PATCH 01/43] Initial support for texturing from framebuffers. --- GPU/GLES/DisplayListInterpreter.cpp | 2 + GPU/GLES/Framebuffer.cpp | 70 +++++++++++++++++++++-------- GPU/GLES/Framebuffer.h | 20 ++++++++- GPU/GLES/StateMapping.cpp | 10 ++--- GPU/GLES/StateMapping.h | 1 - GPU/GLES/TextureCache.cpp | 38 +++++++++++++++- GPU/GLES/TextureCache.h | 16 +++++-- GPU/GLES/TransformPipeline.h | 8 +++- native | 2 +- pspautotests | 2 +- 10 files changed, 136 insertions(+), 33 deletions(-) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index fa7bfa20e0..5ebc5d3ade 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -165,6 +165,8 @@ GLES_GPU::GLES_GPU() shaderManager_ = new ShaderManager(); transformDraw_.SetShaderManager(shaderManager_); transformDraw_.SetTextureCache(&textureCache_); + transformDraw_.SetFramebufferManager(&framebufferManager_); + framebufferManager_.SetTextureCache(&textureCache_); // Sanity check gstate if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) { diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 9c9aadcc5b..09330c3b1b 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -29,6 +29,7 @@ #include "GPU/GPUState.h" #include "GPU/GLES/Framebuffer.h" +#include "GPU/GLES/TextureCache.h" static const char tex_fs[] = "#ifdef GL_ES\n" @@ -66,7 +67,8 @@ static bool MaskedEqual(u32 addr1, u32 addr2) { FramebufferManager::FramebufferManager() : displayFramebufPtr_(0), prevDisplayFramebuf_(0), - prevPrevDisplayFramebuf_(0) + prevPrevDisplayFramebuf_(0), + currentRenderVfb_(0) { glGenTextures(1, &backbufTex); @@ -216,6 +218,13 @@ FramebufferManager::VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { return 0; } +void GetViewportDimensions(int *w, int *h) { + float vpXa = getFloat24(gstate.viewportx1); + float vpYa = getFloat24(gstate.viewporty1); + *w = fabsf(vpXa * 2); + *h = fabsf(vpYa * 2); +} + void FramebufferManager::SetRenderFrameBuffer() { if (!g_Config.bBufferedRendering) return; @@ -226,12 +235,23 @@ void FramebufferManager::SetRenderFrameBuffer() { u32 z_address = (gstate.zbptr & 0xFFE000) | ((gstate.zbwidth & 0xFF0000) << 8); int z_stride = gstate.zbwidth & 0x3C0; + // We guess that the viewport size during the first draw call is an appropriate + // size for a render target. + //UpdateViewportAndProjection(); + // Yeah this is not completely right. but it'll do for now. - int drawing_width = ((gstate.region2) & 0x3FF) + 1; - int drawing_height = ((gstate.region2 >> 10) & 0x3FF) + 1; + //int drawing_width = ((gstate.region2) & 0x3FF) + 1; + //int drawing_height = ((gstate.region2 >> 10) & 0x3FF) + 1; + + // As there are no clear "framebuffer width" and "framebuffer height" registers, + // we need to infer the size of the current framebuffer somehow. Let's try the viewport. + + int drawing_width, drawing_height; + GetViewportDimensions(&drawing_width, &drawing_height); // HACK for first frame where some games don't init things right - if (drawing_width == 1 && drawing_height == 1) { + + if (drawing_width <= 1 && drawing_height <= 1) { drawing_width = 480; drawing_height = 272; } @@ -251,6 +271,9 @@ void FramebufferManager::SetRenderFrameBuffer() { } } + float renderWidthFactor = (float)PSP_CoreParameter().renderWidth / 480.0f; + float renderHeightFactor = (float)PSP_CoreParameter().renderHeight / 272.0f; + // None found? Create one. if (!vfb) { gstate_c.textureChanged = true; @@ -261,6 +284,8 @@ void FramebufferManager::SetRenderFrameBuffer() { vfb->z_stride = z_stride; vfb->width = drawing_width; vfb->height = drawing_height; + vfb->renderWidth = drawing_width * renderWidthFactor; + vfb->renderHeight = drawing_height * renderHeightFactor; vfb->format = fmt; vfb->colorDepth = FBO_8888; @@ -273,15 +298,16 @@ void FramebufferManager::SetRenderFrameBuffer() { //#ifdef ANDROID // vfb->colorDepth = FBO_8888; //#endif - float renderWidthFactor = (float)PSP_CoreParameter().renderWidth / 480.0f; - float renderHeightFactor = (float)PSP_CoreParameter().renderHeight / 272.0f; - vfb->fbo = fbo_create((int)(vfb->width * renderWidthFactor), (int)(vfb->height * renderHeightFactor), 1, true, vfb->colorDepth); + + vfb->fbo = fbo_create(vfb->renderWidth, vfb->renderHeight, 1, true, vfb->colorDepth); + textureCache_->NotifyFramebuffer(vfb->fb_address, vfb->fbo); vfb->last_frame_used = gpuStats.numFrames; vfbs_.push_back(vfb); + fbo_bind_as_render_target(vfb->fbo); glEnable(GL_DITHER); - glstate.viewport.set(0, 0, PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); + glstate.viewport.set(0, 0, vfb->renderWidth, vfb->renderHeight); currentRenderVfb_ = vfb; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); INFO_LOG(HLE, "Creating FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); @@ -292,8 +318,15 @@ void FramebufferManager::SetRenderFrameBuffer() { // Use it as a render target. DEBUG_LOG(HLE, "Switching render target to FBO for %08x", vfb->fb_address); gstate_c.textureChanged = true; + if (vfb->last_frame_used != gpuStats.numFrames) { + // Android optimization + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } + vfb->last_frame_used = gpuStats.numFrames; + fbo_bind_as_render_target(vfb->fbo); + textureCache_->NotifyFramebuffer(vfb->fb_address, vfb->fbo); #ifdef USING_GLES2 // Tiled renderers benefit IMMENSELY from clearing an FBO before rendering // to it. Let's hope this doesn't break too many things... @@ -301,9 +334,8 @@ void FramebufferManager::SetRenderFrameBuffer() { // the first time the buffer is bound on this frame. // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); #endif - glstate.viewport.set(0, 0, PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); + glstate.viewport.set(0, 0, vfb->renderWidth, vfb->renderHeight); currentRenderVfb_ = vfb; - vfb->last_frame_used = gpuStats.numFrames; } } @@ -374,15 +406,16 @@ void FramebufferManager::SetDisplayFramebuffer(u32 framebuf, u32 stride, int for void FramebufferManager::DecimateFBOs() { for (auto iter = vfbs_.begin(); iter != vfbs_.end();) { - VirtualFramebuffer *v = *iter; - if (v == displayFramebuf_ || v == prevDisplayFramebuf_ || v == prevPrevDisplayFramebuf_) { + VirtualFramebuffer *vfb = *iter; + if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) { ++iter; continue; } if ((*iter)->last_frame_used + FBO_OLD_AGE < gpuStats.numFrames) { - INFO_LOG(HLE, "Destroying FBO %i (%i x %i x %i)", v->fb_address, v->width, v->height, v->format) - fbo_destroy(v->fbo); - delete v; + INFO_LOG(HLE, "Destroying FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->format) + textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb->fbo); + fbo_destroy(vfb->fbo); + delete vfb; vfbs_.erase(iter++); } else @@ -392,9 +425,10 @@ void FramebufferManager::DecimateFBOs() { void FramebufferManager::DestroyAllFBOs() { for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *v = *iter; - fbo_destroy(v->fbo); - delete v; + VirtualFramebuffer *vfb = *iter; + textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb->fbo); + fbo_destroy(vfb->fbo); + delete vfb; } vfbs_.clear(); } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 9e5573dacf..84486413df 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -28,6 +28,7 @@ #include "../Globals.h" struct GLSLProgram; +class TextureCache; enum PspDisplayPixelFormat { PSP_DISPLAY_PIXEL_FORMAT_565 = 0, @@ -41,6 +42,10 @@ public: FramebufferManager(); ~FramebufferManager(); + void SetTextureCache(TextureCache *tc) { + textureCache_ = tc; + } + struct VirtualFramebuffer { int last_frame_used; @@ -50,8 +55,10 @@ public: int z_stride; // There's also a top left of the drawing region, but meh... - int width; - int height; + u16 width; + u16 height; + u16 renderWidth; + u16 renderHeight; int format; // virtual, right now they are all RGBA8888 FBOColorDepth colorDepth; @@ -73,6 +80,11 @@ public: void SetDisplayFramebuffer(u32 framebuf, u32 stride, int format); size_t NumVFBs() const { return vfbs_.size(); } + int GetRenderWidth() const { return currentRenderVfb_ ? currentRenderVfb_->renderWidth : 480; } + int GetRenderHeight() const { return currentRenderVfb_ ? currentRenderVfb_->renderHeight : 272; } + int GetTargetWidth() const { return currentRenderVfb_ ? currentRenderVfb_->width : 480; } + int GetTargetHeight() const { return currentRenderVfb_ ? currentRenderVfb_->height : 272; } + private: // Deletes old FBOs. @@ -93,5 +105,9 @@ private: u8 *convBuf; GLSLProgram *draw2dprogram; + + + TextureCache *textureCache_; + bool resized_; }; diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 5c9cdb0b8c..3a1e7c8df4 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -213,11 +213,11 @@ void TransformDrawEngine::ApplyDrawState(int prim) { glstate.depthRange.set(depthRangeMin, depthRangeMax); } -void UpdateViewportAndProjection() { - int renderWidth = PSP_CoreParameter().renderWidth; - int renderHeight = PSP_CoreParameter().renderHeight; - float renderWidthFactor = (float)renderWidth / 480.0f; - float renderHeightFactor = (float)renderHeight / 272.0f; +void TransformDrawEngine::UpdateViewportAndProjection() { + int renderWidth = framebufferManager_->GetRenderWidth(); + int renderHeight = framebufferManager_->GetRenderHeight(); + float renderWidthFactor = (float)renderWidth / framebufferManager_->GetTargetWidth(); + float renderHeightFactor = (float)renderHeight / framebufferManager_->GetTargetHeight(); bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0; // We can probably use these to simply set scissors? Maybe we need to offset by regionX1/Y1 diff --git a/GPU/GLES/StateMapping.h b/GPU/GLES/StateMapping.h index 0bff9bd723..dc720e8eee 100644 --- a/GPU/GLES/StateMapping.h +++ b/GPU/GLES/StateMapping.h @@ -6,5 +6,4 @@ extern const GLint eqLookup[]; extern const GLint cullingMode[]; extern const GLuint ztests[]; -void UpdateViewportAndProjection(); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 88d4daa31f..e061f5df53 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -111,6 +111,31 @@ void TextureCache::InvalidateAll(bool force) { Invalidate(0, 0xFFFFFFFF, force); } +TextureCache::TexCacheEntry *TextureCache::GetEntryAt(u32 texaddr) { + for (auto entry = cache.begin(); entry != cache.end(); ++entry) { + if (entry->second.addr == texaddr) { + return &entry->second; + } + } + return 0; +} + +void TextureCache::NotifyFramebuffer(u32 address, FBO *fbo) { + TexCacheEntry *entry = GetEntryAt(address | 0x04000000); + if (entry) { + INFO_LOG(HLE, "Render to texture detected at %08x!", address); + if (!entry->fbo) + entry->fbo = fbo; + } +} + +void TextureCache::NotifyFramebufferDestroyed(u32 address, FBO *fbo) { + TexCacheEntry *entry = GetEntryAt(address | 0x04000000); + if (entry && entry->fbo) { + entry->fbo = 0; + } +} + static u32 GetClutAddr(u32 clutEntrySize) { return ((gstate.clutaddr & 0xFFFFFF) | ((gstate.clutaddrupper << 8) & 0x0F000000)) + ((gstate.clutformat >> 16) & 0x1f) * clutEntrySize; } @@ -622,6 +647,7 @@ void TextureCache::SetTexture() { ERROR_LOG(G3D, "Unknown texture format %i", format); format = 0; } + bool hasClut = formatUsesClut[format]; u32 clutformat = gstate.clutformat & 3; u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); @@ -630,9 +656,18 @@ void TextureCache::SetTexture() { u32 texhash = texptr ? MiniHash((const u32*)texptr) : 0; u64 cachekey = texaddr ^ texhash; - if (formatUsesClut[format]) + if (hasClut) { cachekey |= (u64) clutaddr << 32; + } + // Check for FBO - slow! + TexCacheEntry *fboEntry = GetEntryAt(texaddr); + if (fboEntry && fboEntry->fbo) { + fbo_bind_color_as_texture(fboEntry->fbo, 0); + UpdateSamplingParams(*fboEntry, false); + return; + } + TexCache::iterator iter = cache.find(cachekey); if (iter != cache.end()) { //Validate the texture here (width, height etc) @@ -693,6 +728,7 @@ void TextureCache::SetTexture() { entry.hash = texhash; entry.format = format; entry.frameCounter = gpuStats.numFrames; + entry.fbo = 0; if (format >= GE_TFMT_CLUT4 && format <= GE_TFMT_CLUT32) { entry.clutformat = clutformat; diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index db4a822f63..0bf593764a 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -18,6 +18,7 @@ #pragma once #include "../Globals.h" +#include "gfx_es2/fbo.h" class TextureCache { @@ -32,21 +33,28 @@ public: void Invalidate(u32 addr, int size, bool force); void InvalidateAll(bool force); + // FramebufferManager keeps TextureCache updated about what regions of memory + // are being rendered to. This is barebones so far. + void NotifyFramebuffer(u32 address, FBO *fbo); + void NotifyFramebufferDestroyed(u32 address, FBO *fbo); + size_t NumLoadedTextures() const { return cache.size(); } private: + struct TexCacheEntry { u32 addr; u32 hash; + FBO *fbo; // if null, not sourced from an FBO. u32 sizeInRAM; int frameCounter; - u32 format; + u8 format; + u8 clutformat; + u16 dim; u32 clutaddr; - u32 clutformat; u32 cluthash; - int dim; u32 texture; //GLuint int invalidHint; u32 fullhash; @@ -63,6 +71,8 @@ private: void *readIndexedTex(int level, u32 texaddr, int bytesPerIndex); void UpdateSamplingParams(TexCacheEntry &entry, bool force); + TexCacheEntry *GetEntryAt(u32 texaddr); + typedef std::map TexCache; // TODO: Speed up by switching to ReadUnchecked*. diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index 8e084a2f97..02a3a8fd9a 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -24,6 +24,8 @@ class LinkedShader; class ShaderManager; class TextureCache; +class FramebufferManager; + struct DecVtxFormat; // States transitions: @@ -100,7 +102,9 @@ public: void SetTextureCache(TextureCache *textureCache) { textureCache_ = textureCache; } - + void SetFramebufferManager(FramebufferManager *fbManager) { + framebufferManager_ = fbManager; + } void InitDeviceObjects(); void DestroyDeviceObjects(); void GLLost(); @@ -111,6 +115,7 @@ public: private: void SoftwareTransformAndDraw(int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertexType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex); void ApplyDrawState(int prim); + void UpdateViewportAndProjection(); // drawcall ID u32 ComputeFastDCID(); @@ -155,6 +160,7 @@ private: // Other ShaderManager *shaderManager_; TextureCache *textureCache_; + FramebufferManager *framebufferManager_; enum { MAX_DEFERRED_DRAW_CALLS = 128 }; DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; diff --git a/native b/native index c21e1ee2cc..09950112d3 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit c21e1ee2cced55bb68edefbb58ba4b30bde807ba +Subproject commit 09950112d3bd60fbfbd877446d5dfe5117b8b9e1 diff --git a/pspautotests b/pspautotests index 31e3915d4e..3870083f15 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 31e3915d4e4302e5e7dc2f6c2a4f191c420f38f9 +Subproject commit 3870083f15c69aa47a159fda148a8df3db6891e4 From 76acd62e6edbfa55118bf66013895c2d75d39f79 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 2 Feb 2013 12:37:41 +0100 Subject: [PATCH 02/43] Try to adjust texture size for fbo dimension --- GPU/GLES/TextureCache.cpp | 12 +++++++++--- native | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index e061f5df53..abd968d292 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -660,11 +660,20 @@ void TextureCache::SetTexture() { cachekey |= (u64) clutaddr << 32; } + int w = 1 << (gstate.texsize[0] & 0xf); + int h = 1 << ((gstate.texsize[0]>>8) & 0xf); + // Check for FBO - slow! TexCacheEntry *fboEntry = GetEntryAt(texaddr); if (fboEntry && fboEntry->fbo) { fbo_bind_color_as_texture(fboEntry->fbo, 0); UpdateSamplingParams(*fboEntry, false); + + int fbow, fboh; + fbo_get_dimensions(fboEntry->fbo, &fbow, &fboh); + + gstate_c.curTextureWidth = w / fbow; // except not - + gstate_c.curTextureHeight = h / fboh; return; } @@ -742,9 +751,6 @@ void TextureCache::SetTexture() { entry.dim = gstate.texsize[0] & 0xF0F; - int w = 1 << (gstate.texsize[0] & 0xf); - int h = 1 << ((gstate.texsize[0]>>8) & 0xf); - // This would overestimate the size in many case so we underestimate instead // to avoid excessive clearing caused by cache invalidations. entry.sizeInRAM = (bitsPerPixel[format < 11 ? format : 0] * bufw * h / 2) / 8; diff --git a/native b/native index 09950112d3..f22ad17d40 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 09950112d3bd60fbfbd877446d5dfe5117b8b9e1 +Subproject commit f22ad17d40c00d9a60bd21f53820012b302d7559 From 88dd9e4b6a2fa46d5efdc7d41deec2270389e679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Wed, 6 Feb 2013 18:49:20 +0100 Subject: [PATCH 03/43] Initial multi language support --- Qt/PPSSPP.pro | 3 ++ Qt/mainwindow.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++ Qt/mainwindow.h | 12 +++++++ Qt/mainwindow.ui | 8 ++++- 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 76ab64cefa..266cd5fafe 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -27,6 +27,9 @@ linux { } } +TRANSLATIONS = languages/PPSSPP_en.ts \ + languages/PPSSPP_pl.ts + # Main SOURCES += ../native/base/QtMain.cpp HEADERS += ../native/base/QtMain.h diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 36e828bce8..117df1bace 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -778,3 +778,92 @@ void MainWindow::on_action_OptionsGamePadControls_triggered() QMessageBox::information(this,"Gamepad","You need to compile with SDL to have Gamepad support.", QMessageBox::Ok); #endif } + +void MainWindow::on_language_changed(QAction *action) +{ + if (0 != action) + { + loadLanguage(action->data().toString()); + } +} + +void switchTranslator(QTranslator &translator, const QString &filename) +{ + qApp->removeTranslator(&translator); + + if (translator.load(filename)) + qApp->installTranslator(&translator); +} + +void MainWindow::loadLanguage(const QString& language) +{ + if (currentLanguage != language) + { + currentLanguage = language; + QLocale locale = QLocale(currentLanguage); + QLocale::setDefault(locale); + QString languageName = QLocale::languageToString(locale.language()); + switchTranslator(translator, QString("PPSSPP_%l.qm").arg(language)); + switchTranslator(qtTranslator, QString("qt_%l.qm").arg(language)); + ui->statusbar->showMessage(tr("Current language changed to %l").arg(languageName)); + } +} + +void MainWindow::createLanguageMenu() +{ + QActionGroup *langGroup = new QActionGroup(ui->menuLanguage); + langGroup->setExclusive(true); + + connect(langGroup, SIGNAL(triggered(QAction *)), this, SLOT(on_language_changed(QAction *))); + + QString defaultLocale = QLocale::system().name(); + defaultLocale.truncate(defaultLocale.lastIndexOf('_')); + languagePath = QApplication::applicationDirPath(); + languagePath.append("/languages"); + QDir langDir(languagePath); + QStringList fileNames = langDir.entryList(QStringList("PPSSPP_*.qm")); + + for (int i = 0; i < fileNames.size(); ++i) + { + QString locale = fileNames[i]; + locale.truncate(locale.lastIndexOf(',')); + locale.remove(0, locale.indexOf('_') + 1); + + QString language = QLocale::languageToString(QLocale(locale).language()); + QAction *action = new QAction(language, this); + action->setCheckable(true); + action->setData(locale); + + ui->menuLanguage->addAction(action); + langGroup->addAction(action); + + if (defaultLocale == locale) + { + action->setChecked(true); + } + } +} + +void MainWindow::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + case QEvent::LocaleChange: + { + QString locale = QLocale::system().name(); + locale.truncate(locale.lastIndexOf('_')); + loadLanguage(locale); + } + break; + default: + break; + } + } + + QMainWindow::changeEvent(event); +} diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index d8a41a0e99..dbb27e3323 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -2,6 +2,7 @@ #define MAINWINDOW_H #include +#include #include "Core/Core.h" #include "input/input_state.h" @@ -135,7 +136,18 @@ private slots: void on_action_OptionsGamePadControls_triggered(); + void on_language_changed(QAction *action); + private: + void loadLanguage(const QString &language); + void createLanguageMenu(); + void changeEvent(QEvent *); + + QTranslator translator; + QTranslator qtTranslator; + QString currentLanguage; + QString languagePath; + Ui::MainWindow *ui; QtEmuGL* w; diff --git a/Qt/mainwindow.ui b/Qt/mainwindow.ui index 1a563fe407..acf08f6a94 100644 --- a/Qt/mainwindow.ui +++ b/Qt/mainwindow.ui @@ -44,7 +44,7 @@ 0 0 800 - 23 + 21 @@ -135,6 +135,11 @@ + + + Language + + @@ -155,6 +160,7 @@ + From 1578ca83edd267d78b8485ae56bd7fd80fe2698a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Thu, 7 Feb 2013 00:04:49 +0100 Subject: [PATCH 04/43] Translations mostly working, initial Polish translation --- Qt/PPSSPP.pro | 4 +- Qt/languages/ppsspp_en.ts | 489 ++++++++++++++++++++++++++++++++++++++ Qt/languages/ppsspp_pl.ts | 489 ++++++++++++++++++++++++++++++++++++++ Qt/mainwindow.cpp | 29 ++- 4 files changed, 1000 insertions(+), 11 deletions(-) create mode 100644 Qt/languages/ppsspp_en.ts create mode 100644 Qt/languages/ppsspp_pl.ts diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 266cd5fafe..3aa67453ad 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -27,8 +27,8 @@ linux { } } -TRANSLATIONS = languages/PPSSPP_en.ts \ - languages/PPSSPP_pl.ts +TRANSLATIONS = languages/ppsspp_en.ts \ + languages/ppsspp_pl.ts # Main SOURCES += ../native/base/QtMain.cpp diff --git a/Qt/languages/ppsspp_en.ts b/Qt/languages/ppsspp_en.ts new file mode 100644 index 0000000000..86b50ae0e6 --- /dev/null +++ b/Qt/languages/ppsspp_en.ts @@ -0,0 +1,489 @@ + + + + + Controls + + + Controls + + + + + Debugger_Disasm + + + Dialog + + + + + Ctr: + + + + + &Go to + + + + + &PC + + + + + &LR + + + + + Tab 1 + + + + + Tab 2 + + + + + &Go + + + + + Stop + + + + + Step &Into + + + + + Step &Over + + + + + S&kip + + + + + Next &HLE + + + + + GamePadDialog + + + Gamepad Configuration + + + + + GamePad List + + + + + Refresh + + + + + Select + + + + + Gamepad Values : + + + + + TextLabel + + + + + Assign Gamepad input + + + + + to PSP button/axis + + + + + Assign + + + + + Press buttons on your gamePad to verify mapping : + + + + + MainWindow + + + PPSSPP + + + + + &File + + + + + &Emulation + + + + + Debu&g + + + + + &Options + + + + + &Log Levels + + + + + G3D + + + + + HLE + + + + + Default + + + + + Zoom + + + + + Language + + + + + &Help + + + + + &Open... + + + + + &Close + + + + + - + + + + + Quickload state + + + + + F4 + + + + + Quicksave state + + + + + F2 + + + + + &Load State File... + + + + + &Save State File... + + + + + E&xit + + + + + &Run + + + + + F7 + + + + + &Pause + + + + + F8 + + + + + R&eset + + + + + &Interpreter + + + + + &Slightly Faster Interpreter + + + + + &Dynarec + + + + + Load &Map File... + + + + + &Save Map File... + + + + + &Reset Symbol Table + + + + + &Disassembly + + + + + Ctrl+D + + + + + &Log Console + + + + + Ctrl+L + + + + + Memory &View... + + + + + Ctrl+M + + + + + Keyboard &Controls + + + + + &Toggle Full Screen + + + + + F12 + + + + + &Buffered Rendering + + + + + F5 + + + + + &Hardware Transform + + + + + F6 + + + + + &Linear Filtering + + + + + &Wireframe (experimental) + + + + + &Display Raw Framebuffer + + + + + &Show Debug Statistics + + + + + Screen &1x + + + + + Ctrl+1 + + + + + Screen &2x + + + + + Ctrl+2 + + + + + Screen &3x + + + + + Ctrl+3 + + + + + Screen &4x + + + + + Ctrl+4 + + + + + &Fast Memory (dynarec, unstable) + + + + + &Ignore illegal reads/writes + + + + + &Go to http://www.ppsspp.org/ + + + + + &About PPSSPP... + + + + + &Use VBO + + + + + + + Debug + + + + + + + Warning + + + + + + + Error + + + + + + + Info + + + + + GamePad Controls + + + + + Current language changed to %1 + + + + diff --git a/Qt/languages/ppsspp_pl.ts b/Qt/languages/ppsspp_pl.ts new file mode 100644 index 0000000000..c67ff0631b --- /dev/null +++ b/Qt/languages/ppsspp_pl.ts @@ -0,0 +1,489 @@ + + + + + Controls + + + Controls + + + + + Debugger_Disasm + + + Dialog + + + + + Ctr: + + + + + &Go to + + + + + &PC + + + + + &LR + + + + + Tab 1 + + + + + Tab 2 + + + + + &Go + + + + + Stop + + + + + Step &Into + + + + + Step &Over + + + + + S&kip + + + + + Next &HLE + + + + + GamePadDialog + + + Gamepad Configuration + Konfiguracja kontrolera + + + + GamePad List + Lista kontrolerów + + + + Refresh + Odśwież + + + + Select + Wybierz + + + + Gamepad Values : + + + + + TextLabel + + + + + Assign Gamepad input + Przypisz przycisk + + + + to PSP button/axis + do przycisku/osi PSP + + + + Assign + Przypisz + + + + Press buttons on your gamePad to verify mapping : + Naciśnij przyciski na kontrolerze: + + + + MainWindow + + + PPSSPP + PPSSPP + + + + &File + &Plik + + + + &Emulation + &Emulacja + + + + Debu&g + &Debugger + + + + &Options + &Opcje + + + + &Log Levels + &Poziomy logowania + + + + G3D + G3D + + + + HLE + HLE + + + + Default + Domyślne + + + + Zoom + Zoom + + + + Language + Język + + + + &Help + Pomo&c + + + + &Open... + &Otwórz... + + + + &Close + &Zamknij + + + + - + + + + + Quickload state + Wczytaj stan + + + + F4 + F4 + + + + Quicksave state + Zapisz stan + + + + F2 + F2 + + + + &Load State File... + &Wczytaj plik stanu... + + + + &Save State File... + &Zapisz plik stanu... + + + + E&xit + Wyj&dź + + + + &Run + &Uruchom + + + + F7 + F7 + + + + &Pause + &Pauza + + + + F8 + F8 + + + + R&eset + &Reset + + + + &Interpreter + &Interpreter + + + + &Slightly Faster Interpreter + &Szybszy interpreter + + + + &Dynarec + R&ekompilacja (Dynarec) + + + + Load &Map File... + &Wczytaj plik mapy... + + + + &Save Map File... + &Zapisz plik mapy... + + + + &Reset Symbol Table + Zresetuj &tablicę symboli + + + + &Disassembly + &Dekompiluj + + + + Ctrl+D + Ctrl+D + + + + &Log Console + &Konsola logowania + + + + Ctrl+L + Ctrl+L + + + + Memory &View... + Widok &pamięci... + + + + Ctrl+M + Ctrl+M + + + + Keyboard &Controls + + + + + &Toggle Full Screen + &Pełny ekran + + + + F12 + F12 + + + + &Buffered Rendering + &Buffered rendering + + + + F5 + F5 + + + + &Hardware Transform + &Hardware Transform + + + + F6 + F6 + + + + &Linear Filtering + &Linear Filtering + + + + &Wireframe (experimental) + + + + + &Display Raw Framebuffer + + + + + &Show Debug Statistics + + + + + Screen &1x + + + + + Ctrl+1 + + + + + Screen &2x + + + + + Ctrl+2 + + + + + Screen &3x + + + + + Ctrl+3 + + + + + Screen &4x + + + + + Ctrl+4 + + + + + &Fast Memory (dynarec, unstable) + + + + + &Ignore illegal reads/writes + + + + + &Go to http://www.ppsspp.org/ + + + + + &About PPSSPP... + + + + + &Use VBO + + + + + + + Debug + + + + + + + Warning + + + + + + + Error + + + + + + + Info + + + + + GamePad Controls + + + + + Current language changed to %1 + Zmieniono język na %1 + + + diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 9f5d1483eb..125043c00b 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -48,6 +48,7 @@ MainWindow::MainWindow(QWidget *parent) : DialogManager::AddDlg(vfpudlg = new CVFPUDlg(_hInstance, hwndMain, currentDebugMIPS)); */ // Update(); + createLanguageMenu(); UpdateMenus(); int zoom = g_Config.iWindowZoom; @@ -804,9 +805,7 @@ void MainWindow::loadLanguage(const QString& language) QLocale locale = QLocale(currentLanguage); QLocale::setDefault(locale); QString languageName = QLocale::languageToString(locale.language()); - switchTranslator(translator, QString("PPSSPP_%l.qm").arg(language)); - switchTranslator(qtTranslator, QString("qt_%l.qm").arg(language)); - ui->statusbar->showMessage(tr("Current language changed to %l").arg(languageName)); + switchTranslator(translator, QString("languages/ppsspp_%1.qm").arg(language)); } } @@ -822,15 +821,25 @@ void MainWindow::createLanguageMenu() languagePath = QApplication::applicationDirPath(); languagePath.append("/languages"); QDir langDir(languagePath); - QStringList fileNames = langDir.entryList(QStringList("PPSSPP_*.qm")); + QStringList fileNames = langDir.entryList(QStringList("ppsspp_*.qm")); + + if (fileNames.size() == 0) + { + QAction *action = new QAction(tr("No translations"), this); + action->setCheckable(false); + action->setDisabled(true); + ui->menuLanguage->addAction(action); + langGroup->addAction(action); + } for (int i = 0; i < fileNames.size(); ++i) { QString locale = fileNames[i]; - locale.truncate(locale.lastIndexOf(',')); + locale.truncate(locale.lastIndexOf('.')); locale.remove(0, locale.indexOf('_') + 1); - QString language = QLocale::languageToString(QLocale(locale).language()); + //QString language = QLocale::languageToString(QLocale(locale).language()); + QString language = QLocale(locale).nativeLanguageName(); QAction *action = new QAction(language, this); action->setCheckable(true); action->setData(locale); @@ -838,15 +847,19 @@ void MainWindow::createLanguageMenu() ui->menuLanguage->addAction(action); langGroup->addAction(action); - if (defaultLocale == locale) + // TODO check en as default until we save language to config + if ("en" == locale) { action->setChecked(true); + currentLanguage = "en"; } } } void MainWindow::changeEvent(QEvent *event) { + QMainWindow::changeEvent(event); + if (0 != event) { switch (event->type()) @@ -865,6 +878,4 @@ void MainWindow::changeEvent(QEvent *event) break; } } - - QMainWindow::changeEvent(event); } From ff9b38f03d329936001ecb056ea04174ea4d303d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 8 Feb 2013 00:18:29 +0100 Subject: [PATCH 05/43] Make GamePadDialog class translatable --- Qt/gamepaddialog.cpp | 84 ++++++++------ Qt/languages/ppsspp_en.ts | 234 +++++++++++++++++++++++++++++++++++++- 2 files changed, 281 insertions(+), 37 deletions(-) diff --git a/Qt/gamepaddialog.cpp b/Qt/gamepaddialog.cpp index 858a17f9db..d90421a9b3 100644 --- a/Qt/gamepaddialog.cpp +++ b/Qt/gamepaddialog.cpp @@ -16,26 +16,26 @@ struct GamePadInfo // Initial values are PS3 controller GamePadInfo GamepadPadMapping[] = { - {0, 14, 0, "Prev_X", "Cross"}, //A - {0, 13, 0, "Prev_O", "Circle"}, //B - {0, 15, 0, "Prev_S", "Square"}, //X - {0, 12, 0, "Prev_T", "Triangle"}, //Y - {0, 10, 0, "Prev_LT", "Left Trigger"}, //LBUMPER - {0, 11, 0, "Prev_RT", "Right Trigger"}, //RBUMPER - {0, 3, 0, "Prev_Start", "Start"}, //START - {0, 0, 0, "Prev_Select", "Select"}, //SELECT - {0, 4, 0, "Prev_Up", "Up"}, //UP - {0, 6, 0, "Prev_Down", "Down"}, //DOWN - {0, 7, 0, "Prev_Left", "Left"}, //LEFT - {0, 5, 0, "Prev_Right", "Right"}, //RIGHT - {0, 0, 0, ""}, //MENU (event) - {0, 16, 0, "Prev_Home", "Home"}, //BACK + {0, 14, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_X"), QT_TRANSLATE_NOOP("gamepadMapping", "Cross")}, //A + {0, 13, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_O"), QT_TRANSLATE_NOOP("gamepadMapping", "Circle")}, //B + {0, 15, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_S"), QT_TRANSLATE_NOOP("gamepadMapping", "Square")}, //X + {0, 12, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_T"), QT_TRANSLATE_NOOP("gamepadMapping", "Triangle")}, //Y + {0, 10, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_LT"), QT_TRANSLATE_NOOP("gamepadMapping", "Left Trigger")}, //LBUMPER + {0, 11, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_RT"), QT_TRANSLATE_NOOP("gamepadMapping", "Right Trigger")}, //RBUMPER + {0, 3, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Start"), QT_TRANSLATE_NOOP("gamepadMapping", "Start")}, //START + {0, 0, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Select"), QT_TRANSLATE_NOOP("gamepadMapping", "Select")}, //SELECT + {0, 4, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Up"), QT_TRANSLATE_NOOP("gamepadMapping", "Up")}, //UP + {0, 6, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Down"), QT_TRANSLATE_NOOP("gamepadMapping", "Down")}, //DOWN + {0, 7, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Left"), QT_TRANSLATE_NOOP("gamepadMapping", "Left")}, //LEFT + {0, 5, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Right"), QT_TRANSLATE_NOOP("gamepadMapping", "Right")}, //RIGHT + {0, 0, 0, QT_TRANSLATE_NOOP("gamepadMapping", "")}, //MENU (event) + {0, 16, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Home"), QT_TRANSLATE_NOOP("gamepadMapping", "Home")}, //BACK // Special case for analog stick - {1, 0, -1, "Prev_ALeft", "Stick left"}, - {1, 0, 1, "Prev_ARight", "Stick right"}, - {1, 1, -1, "Prev_AUp", "Stick up"}, - {1, 1, 1, "Prev_ADown", "Stick bottom"} + {1, 0, -1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_ALeft"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick left")}, + {1, 0, 1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_ARight"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick right")}, + {1, 1, -1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_AUp"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick up")}, + {1, 1, 1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_ADown"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick bottom")} }; // id for mapping in config start at offset 200 to not get over key mapping @@ -65,7 +65,7 @@ GamePadDialog::GamePadDialog(InputState* state, QWidget *parent) : for(int i=0;i<18;i++) { - QLabel* labelPreview = findChild(GamepadPadMapping[i].ViewLabelName); + QLabel* labelPreview = findChild(tr(GamepadPadMapping[i].ViewLabelName.toStdString().c_str())); if(labelPreview) { labelPreview->setVisible(false); @@ -106,6 +106,20 @@ void GamePadDialog::showEvent(QShowEvent *) #endif } +void GamePadDialog::changeEvent(QEvent *event) +{ + QDialog::changeEvent(event); + + if (0 != event) + { + if (event->type() == QEvent::LanguageChange) + { + ui->retranslateUi(this); + on_refreshListBtn_clicked(); + } + } +} + void GamePadDialog::releaseLock() { EmuThread_LockDraw(false); @@ -117,7 +131,7 @@ void GamePadDialog::on_refreshListBtn_clicked() if(m_joystick) { SDL_JoystickClose(m_joystick); - ui->JoyName->setText("No GamePad"); + ui->JoyName->setText(tr("No gamepad")); m_joystick = 0; } SDL_QuitSubSystem(SDL_INIT_JOYSTICK); @@ -129,7 +143,7 @@ void GamePadDialog::on_refreshListBtn_clicked() { QListWidgetItem* item = new QListWidgetItem(); QString padName = SDL_JoystickName(i); - if(padName == "") padName = "Unknown GamePad"; + if(padName == "") padName = tr("Unknown gamepad"); item->setText(padName); item->setData(Qt::UserRole,i); ui->GamePadList->addItem(item); @@ -165,7 +179,7 @@ void GamePadDialog::pollJoystick() } else if(GamepadPadMapping[i].mapping_type == 2) val = SDL_JoystickGetHat(m_joystick,GamepadPadMapping[i].mapping_in); - QLabel* labelPreview = findChild(GamepadPadMapping[i].ViewLabelName); + QLabel* labelPreview = findChild(tr(GamepadPadMapping[i].ViewLabelName.toStdString().c_str())); if(labelPreview) { labelPreview->setVisible(val != 0); @@ -197,7 +211,7 @@ void GamePadDialog::pollJoystick() } else if(GamepadPadMapping[i].mapping_type == 2) val = SDL_JoystickGetHat(m_joystick,GamepadPadMapping[i].mapping_in); - QLabel* labelPreview = findChild(GamepadPadMapping[i].ViewLabelName); + QLabel* labelPreview = findChild(tr(GamepadPadMapping[i].ViewLabelName.toStdString().c_str())); if(labelPreview) { labelPreview->setVisible(val != 0); @@ -270,7 +284,7 @@ void GamePadDialog::on_SelectPadBtn_clicked() ui->comboPSPButton->clear(); QTreeWidgetItem* buttonItem = new QTreeWidgetItem(); - buttonItem->setText(0,"Buttons"); + buttonItem->setText(0,tr("Buttons")); ui->padValues->addTopLevelItem(buttonItem); for(int i = 0; i < SDL_JoystickNumButtons(m_joystick); i++) @@ -284,37 +298,37 @@ void GamePadDialog::on_SelectPadBtn_clicked() buttonItem->addChild(item); int id = i << 8; - ui->comboPadInput->addItem("Button "+QVariant(i).toString(),GetIntFromMapping(i,0,0)); + ui->comboPadInput->addItem(tr("Button %1").arg(i),GetIntFromMapping(i,0,0)); } QTreeWidgetItem* axesItem = new QTreeWidgetItem(); - axesItem->setText(0,"Axes"); + axesItem->setText(0,tr("Axes")); ui->padValues->addTopLevelItem(axesItem); for(int i = 0; i < SDL_JoystickNumAxes(m_joystick); i++) { QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QVariant(i).toString()+" Neg"); + item->setText(0,tr("%1 Neg").arg(i)); item->setText(1,QVariant(0).toString()); item->setData(0, Qt::UserRole,1); item->setData(0, Qt::UserRole+1,i); item->setData(0, Qt::UserRole+2,-1); axesItem->addChild(item); - ui->comboPadInput->addItem("Axes "+QVariant(i).toString()+" Neg",GetIntFromMapping(i,1,-1)); + ui->comboPadInput->addItem(tr("Axes %1 Neg").arg(i),GetIntFromMapping(i,1,-1)); item = new QTreeWidgetItem(); - item->setText(0,QVariant(i).toString()+" Pos"); + item->setText(0,tr("%1 Pos").arg(i)); item->setText(1,QVariant(0).toString()); item->setData(0, Qt::UserRole,1); item->setData(0, Qt::UserRole+1,i); item->setData(0, Qt::UserRole+2,1); axesItem->addChild(item); - ui->comboPadInput->addItem("Axes "+QVariant(i).toString()+" Pos",GetIntFromMapping(i,1,1)); + ui->comboPadInput->addItem(tr("Axes %1 Pos").arg(i),GetIntFromMapping(i,1,1)); } QTreeWidgetItem* hatsItem = new QTreeWidgetItem(); - hatsItem->setText(0,"Hats"); + hatsItem->setText(0,tr("Hats")); ui->padValues->addTopLevelItem(hatsItem); for(int i = 0; i < SDL_JoystickNumHats(m_joystick); i++) @@ -327,14 +341,14 @@ void GamePadDialog::on_SelectPadBtn_clicked() item->setData(0, Qt::UserRole+2,0); hatsItem->addChild(item); - ui->comboPadInput->addItem("Button "+QVariant(i).toString(),GetIntFromMapping(i,2,0)); + ui->comboPadInput->addItem(tr("Button %1").arg(i),GetIntFromMapping(i,2,0)); } for(int i = 0; i < 18; i++) { if(GamepadPadMapping[i].Name != "") { - ui->comboPSPButton->addItem(GamepadPadMapping[i].Name,i); + ui->comboPSPButton->addItem(QApplication::translate("gamepadMapping", GamepadPadMapping[i].Name.toStdString().c_str()),i); } } @@ -349,9 +363,9 @@ void GamePadDialog::SetViewMode() ui->refreshListBtn->setEnabled(true); ui->SelectPadBtn->setEnabled(true); if(!m_joystick) - ui->JoyName->setText("No GamePad"); + ui->JoyName->setText(tr("No gamepad")); else - ui->JoyName->setText(QString("Current gamepad : ")+SDL_JoystickName(m_joyId)+""); + ui->JoyName->setText(tr("Current gamepad: %1").arg(SDL_JoystickName(m_joyId))); #endif } diff --git a/Qt/languages/ppsspp_en.ts b/Qt/languages/ppsspp_en.ts index 86b50ae0e6..444a6cb944 100644 --- a/Qt/languages/ppsspp_en.ts +++ b/Qt/languages/ppsspp_en.ts @@ -129,6 +129,63 @@ Press buttons on your gamePad to verify mapping : + + + + <b>No gamepad</b> + + + + + <b>Unknown gamepad</b> + + + + + Buttons + + + + + + Button %1 + + + + + Axes + + + + + %1 Neg + + + + + Axes %1 Neg + + + + + %1 Pos + + + + + Axes %1 Pos + + + + + Hats + + + + + <b>Current gamepad: %1</b> + + MainWindow @@ -481,8 +538,181 @@ - - Current language changed to %1 + + No translations + + + + + gamepadMapping + + + Prev_X + + + + + Cross + + + + + Prev_O + + + + + Circle + + + + + Prev_S + + + + + Square + + + + + Prev_T + + + + + Triangle + + + + + Prev_LT + + + + + Left Trigger + + + + + Prev_RT + + + + + Right Trigger + + + + + Prev_Start + + + + + Start + + + + + Prev_Select + + + + + Select + + + + + Prev_Up + + + + + Up + + + + + Prev_Down + + + + + Down + + + + + Prev_Left + + + + + Left + + + + + Prev_Right + + + + + Right + + + + + Prev_Home + + + + + Home + + + + + Prev_ALeft + + + + + Stick left + + + + + Prev_ARight + + + + + Stick right + + + + + Prev_AUp + + + + + Stick up + + + + + Prev_ADown + + + + + Stick bottom From 74348b08dee8b6bac37d6eee3d6e73608891e98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 8 Feb 2013 00:26:33 +0100 Subject: [PATCH 06/43] Improve Polish translation --- .gitignore | 3 + Qt/languages/ppsspp_pl.ts | 280 ++++++++++++++++++++++++++++++++++---- 2 files changed, 258 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 5e6b96b51c..a99238ff41 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ Windows/ipch # For ppsspp.ini, etc. *.ini +# Qt Linguist files +*.qm + Logs Memstick diff --git a/Qt/languages/ppsspp_pl.ts b/Qt/languages/ppsspp_pl.ts index c67ff0631b..a25b66f8fd 100644 --- a/Qt/languages/ppsspp_pl.ts +++ b/Qt/languages/ppsspp_pl.ts @@ -102,12 +102,12 @@ Gamepad Values : - + Wartości przycisków/osi: TextLabel - + @@ -129,6 +129,63 @@ Press buttons on your gamePad to verify mapping : Naciśnij przyciski na kontrolerze: + + + + <b>No gamepad</b> + <b>Nie wykryto pada</b> + + + + <b>Unknown gamepad</b> + <b>Nieznany pad</b> + + + + Buttons + Przyciski + + + + + Button %1 + Przycisk %1 + + + + Axes + Osie + + + + %1 Neg + %1 zanegowany + + + + Axes %1 Neg + Oś %1 zanegowana + + + + %1 Pos + %1 pozycja + + + + Axes %1 Pos + Pozycja osi %1 + + + + Hats + + + + + <b>Current gamepad: %1</b> + <b>Wybrany pad: %1</b> + MainWindow @@ -160,7 +217,7 @@ &Log Levels - &Poziomy logowania + P&oziomy logowania @@ -330,7 +387,7 @@ Keyboard &Controls - + Ustawienia &klawiatury @@ -380,110 +437,283 @@ &Show Debug Statistics - + Pokaż &statystyki emulacji Screen &1x - + &1x Ctrl+1 - + Ctrl+1 Screen &2x - + &2x Ctrl+2 - + Ctrl+2 Screen &3x - + &3x Ctrl+3 - + Ctrl+3 Screen &4x - + &4x Ctrl+4 - + Ctrl+4 &Fast Memory (dynarec, unstable) - + &Fast memory (wymagany Dynarec, niestabilne) &Ignore illegal reads/writes - + &Ignoruj nieprawidłowe odczyty/zapisy &Go to http://www.ppsspp.org/ - + &Idź do http://www.ppsspp.org &About PPSSPP... - + &O PPSSPP... &Use VBO - + Użyj &VBO Debug - + Debug Warning - + Ostrzeżenia Error - + Błędy Info - + Info GamePad Controls + &Ustawienia pada + + + + No translations + + + + + gamepadMapping + + + Prev_X - - Current language changed to %1 - Zmieniono język na %1 + + Cross + Krzyżyk + + + + Prev_O + + + + + Circle + Kółko + + + + Prev_S + + + + + Square + Kwadrat + + + + Prev_T + + + + + Triangle + Trójkąt + + + + Prev_LT + + + + + Left Trigger + Lewy trigger + + + + Prev_RT + + + + + Right Trigger + Prawy trigger + + + + Prev_Start + + + + + Start + Start + + + + Prev_Select + + + + + Select + Select + + + + Prev_Up + + + + + Up + Góra + + + + Prev_Down + + + + + Down + Dół + + + + Prev_Left + + + + + Left + Lewo + + + + Prev_Right + + + + + Right + Prawo + + + + Prev_Home + + + + + Home + Klawisz Home + + + + Prev_ALeft + + + + + Stick left + Lewo (analog) + + + + Prev_ARight + + + + + Stick right + Prawo (analog) + + + + Prev_AUp + + + + + Stick up + Góra (analog) + + + + Prev_ADown + + + + + Stick bottom + Dół (analog) From f875e3699fa90a658a8ea69e94607c2fb4231986 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 Jan 2013 16:35:34 -0800 Subject: [PATCH 07/43] Handle the emit screenshot headless command. But, it doesn't work. Shouldn't the vram have the graphics? --- Core/HLE/sceDisplay.cpp | 12 ++++++++++++ Core/HLE/sceDisplay.h | 3 +++ Core/HLE/sceIo.cpp | 12 ++++++++++++ Core/Host.h | 1 + headless/Headless.cpp | 9 +++++++++ headless/StubHost.h | 1 + headless/WindowsHeadlessHost.cpp | 10 ++++++++++ headless/WindowsHeadlessHost.h | 3 +++ test.py | 2 ++ 9 files changed, 53 insertions(+) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 8e61522cee..da021db024 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -357,6 +357,18 @@ u32 sceDisplaySetFramebuf() { return 0; } +bool __DisplayGetFramebuf(u8 **topaddr, u32 *linesize, u32 *pixelFormat, int mode) { + const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf; + if (topaddr != NULL) + *topaddr = Memory::GetPointer(fbState.topaddr); + if (linesize != NULL) + *linesize = fbState.pspFramebufLinesize; + if (pixelFormat != NULL) + *pixelFormat = fbState.pspFramebufFormat; + + return true; +} + u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode) { const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf; DEBUG_LOG(HLE,"sceDisplayGetFramebuf(*%08x = %08x, *%08x = %08x, *%08x = %08x, %i)", diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index c75faf93f7..fd6de956e3 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -26,6 +26,9 @@ void Register_sceDisplay(); // will return true once after every end-of-frame. bool __DisplayFrameDone(); +// Get information about the current framebuffer. +bool __DisplayGetFramebuf(u8 **topaddr, u32 *linesize, u32 *pixelFormat, int mode); + typedef void (*VblankCallback)(); // Listen for vblank events. Only register during init. void __DisplayListenVblank(VblankCallback callback); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 15c535cf4c..19a3a5cf4c 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -38,6 +38,9 @@ #include "sceKernelMemory.h" #include "sceKernelThread.h" +// For headless screenshots. +#include "sceDisplay.h" + #define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002 #define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081 @@ -782,6 +785,15 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, SaveState::Verify(); // TODO: Maybe save/load to a file just to be sure? return 0; + + case 0x20: // EMULATOR_DEVCTL__EMIT_SCREENSHOT + u8 *topaddr; + u32 linesize, pixelFormat; + + __DisplayGetFramebuf(&topaddr, &linesize, &pixelFormat, 0); + // TODO: Convert based on pixel format / mode / something? + host->SendDebugScreenshot(topaddr, linesize, 272); + return 0; } ERROR_LOG(HLE, "sceIoDevCtl: UNKNOWN PARAMETERS"); diff --git a/Core/Host.h b/Core/Host.h index 29cc5b2fc4..d426289d14 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -63,6 +63,7 @@ public: // Used for headless. virtual void SendDebugOutput(const std::string &output) {} + virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) {} }; extern Host *host; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index b01338982e..e6ff10335b 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -59,7 +59,10 @@ void printUsage(const char *progname, const char *reason) HEADLESSHOST_CLASS h1; HeadlessHost h2; if (typeid(h1) != typeid(h2)) + { fprintf(stderr, " --graphics use the full gpu backend (slower)\n"); + fprintf(stderr, " --screenshot=FILE compare against a screenshot\n"); + } fprintf(stderr, " -f use the fast interpreter\n"); fprintf(stderr, " -j use jit (overrides -f)\n"); @@ -77,6 +80,7 @@ int main(int argc, const char* argv[]) const char *bootFilename = 0; const char *mountIso = 0; + const char *screenshotFilename = 0; bool readMount = false; for (int i = 1; i < argc; i++) @@ -99,6 +103,8 @@ int main(int argc, const char* argv[]) autoCompare = true; else if (!strcmp(argv[i], "--graphics")) useGraphics = true; + else if (!strncmp(argv[i], "--screenshot=", strlen("--screenshot=")) && strlen(argv[i]) > strlen("--screenshot=")) + screenshotFilename = argv[i] + strlen("--screenshot="); else if (bootFilename == 0) bootFilename = argv[i]; else @@ -174,6 +180,9 @@ int main(int argc, const char* argv[]) host->BootDone(); + if (screenshotFilename != 0) + headlessHost->SetComparisonScreenshot(screenshotFilename); + coreState = CORE_RUNNING; while (coreState == CORE_RUNNING) { diff --git a/headless/StubHost.h b/headless/StubHost.h index 9445aee06c..1533c381c3 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -50,6 +50,7 @@ public: virtual bool AttemptLoadSymbolMap() {return false;} virtual void SendDebugOutput(const std::string &output) { printf("%s", output.c_str()); } + virtual void SetComparisonScreenshot(const std::string &filename) {} virtual bool isGLWorking() { return false; } }; \ No newline at end of file diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 606235c26a..689afbf800 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -89,6 +89,16 @@ void WindowsHeadlessHost::SendDebugOutput(const std::string &output) OutputDebugString(output.c_str()); } +void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) +{ + fprintf_s(out, "Got a screenshot: %d/%d\n", w, h); +} + +void WindowsHeadlessHost::SetComparisonScreenshot(const std::string &filename) +{ + comparisonScreenshot = filename; +} + void WindowsHeadlessHost::InitGL() { glOkay = false; diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 28e51ee543..5abc20ca05 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -35,6 +35,8 @@ public: virtual bool isGLWorking() { return glOkay; } virtual void SendDebugOutput(const std::string &output); + virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h); + virtual void SetComparisonScreenshot(const std::string &filename); private: bool ResizeGL(); @@ -45,4 +47,5 @@ private: HDC hDC; HGLRC hRC; FILE *out; + std::string comparisonScreenshot; }; \ No newline at end of file diff --git a/test.py b/test.py index 52b2b906b1..983e47db6a 100755 --- a/test.py +++ b/test.py @@ -222,6 +222,8 @@ def run_tests(test_list, args): cmdline = [PPSSPP_EXE, elf_filename] cmdline.extend([i for i in args if i not in ['-v', '-g']]) + if os.path.exists(expected_filename + ".bmp"): + cmdline.extend(["--screenshot=" + expected_filename + ".bmp", "--graphics"]) c = Command(cmdline) c.run(TIMEOUT) From eb6258d4d87269a9334d110ff439eacee734f3b2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 6 Feb 2013 08:10:00 -0800 Subject: [PATCH 08/43] Simple Windows-only headless screenshot compare. --- headless/WindowsHeadlessHost.cpp | 50 +++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 689afbf800..81864f0942 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -91,7 +91,55 @@ void WindowsHeadlessHost::SendDebugOutput(const std::string &output) void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) { - fprintf_s(out, "Got a screenshot: %d/%d\n", w, h); + // We ignore the current framebuffer parameters and just grab the full screen. + const static int FRAME_WIDTH = 512; + const static int FRAME_HEIGHT = 272; + u32 *pixels = (u32 *) calloc(FRAME_WIDTH * FRAME_HEIGHT, sizeof(u32)); + u32 *reference = (u32 *) calloc(FRAME_WIDTH * FRAME_HEIGHT, sizeof(u32)); + + // TODO: Maybe the GPU should do this? + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + BITMAPFILEHEADER header; + BITMAPINFOHEADER infoHeader; + FILE *bmp = fopen(comparisonScreenshot.c_str(), "rb"); + if (bmp) + { + fread(&header, sizeof(header), 1, bmp); + fread(&infoHeader, sizeof(infoHeader), 1, bmp); + fread(reference, sizeof(u32), FRAME_WIDTH * FRAME_HEIGHT, bmp); + fclose(bmp); + } + else + fprintf_s(out, "Unable to read screenshot: %s\n", comparisonScreenshot.c_str()); + + // TODO: Better error rate and move to headless/shared between platforms. + int errors = 0; + for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; ++i) + { + // Ignore alpha. + errors += (pixels[i] & 0xFFFFFF) != (reference[i] & 0xFFFFFF) ? 1 : 0; + } + + if (errors != 0) + { + fprintf_s(out, "Screenshot error: %f%%\n", (float) errors * 100.0f / (float) (FRAME_WIDTH * FRAME_HEIGHT)); + + FILE *saved = fopen("__testfailure.bmp", "wb"); + if (saved) + { + fwrite(&header, sizeof(header), 1, saved); + fwrite(&infoHeader, sizeof(infoHeader), 1, saved); + fwrite(pixels, sizeof(u32), FRAME_WIDTH * FRAME_HEIGHT, saved); + fclose(saved); + + fprintf_s(out, "Actual output written to: __testfailure.bmp\n"); + } + } + + free(pixels); + free(reference); } void WindowsHeadlessHost::SetComparisonScreenshot(const std::string &filename) From b24d41b1561fc10820d83a54e7b1f9222a6a2983 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 7 Feb 2013 01:34:14 -0800 Subject: [PATCH 09/43] Bitmaps are stored BGR, so let's read it that way. --- headless/WindowsHeadlessHost.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 81864f0942..074d8b38fa 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -99,7 +99,7 @@ void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) // TODO: Maybe the GPU should do this? glReadBuffer(GL_FRONT); - glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, pixels); BITMAPFILEHEADER header; BITMAPINFOHEADER infoHeader; @@ -119,6 +119,7 @@ void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; ++i) { // Ignore alpha. + // TODO: Error threshold? errors += (pixels[i] & 0xFFFFFF) != (reference[i] & 0xFFFFFF) ? 1 : 0; } From 6a72b0d3259ce2ed986882a4a34af8897c084bb7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 00:22:37 -0800 Subject: [PATCH 10/43] Separate out the actual screenshot comparison. Fixes #109. --- CMakeLists.txt | 6 ++- headless/Compare.cpp | 77 +++++++++++++++++++++++++++++++ headless/Compare.h | 23 +++++++++ headless/Headless.cpp | 13 +----- headless/Headless.vcxproj | 2 + headless/Headless.vcxproj.filters | 2 + headless/WindowsHeadlessHost.cpp | 44 +++++++----------- 7 files changed, 128 insertions(+), 39 deletions(-) create mode 100644 headless/Compare.cpp create mode 100644 headless/Compare.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c567215010..37509ed442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -918,7 +918,11 @@ if(WIN32) endif() if(HEADLESS) - add_executable(PPSSPPHeadless headless/Headless.cpp headless/StubHost.h) + add_executable(PPSSPPHeadless + headless/Headless.cpp + headless/StubHost.h + headless/Compare.cpp + headless/Compare.h) target_link_libraries(PPSSPPHeadless ${CoreLibName} ${COCOA_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) setup_target_project(PPSSPPHeadless headless) diff --git a/headless/Compare.cpp b/headless/Compare.cpp new file mode 100644 index 0000000000..02252d9833 --- /dev/null +++ b/headless/Compare.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Compare.h" +#include "FileUtil.h" + +#include + +bool CompareOutput(const std::string bootFilename) +{ + std::string expect_filename = bootFilename.substr(bootFilename.length() - 4) + ".expected"; + if (File::Exists(expect_filename)) + { + // TODO: Do the compare here + return true; + } + else + { + fprintf(stderr, "Expectation file %s not found", expect_filename.c_str()); + return false; + } +} + +inline int ComparePixel(u32 pix1, u32 pix2) +{ + // For now, if they're different at all except alpha, it's an error. + if ((pix1 & 0xFFFFFF) != (pix2 & 0xFFFFFF)) + return 1; + return 0; +} + +double CompareScreenshot(const u8 *pixels, int w, int h, int stride, const std::string screenshotFilename, std::string &error) +{ + u32 *pixels32 = (u32 *) pixels; + // We assume the bitmap is the specified size, not including whatever stride. + u32 *reference = (u32 *) calloc(w * h, sizeof(u32)); + + FILE *bmp = fopen(screenshotFilename.c_str(), "rb"); + if (bmp) + { + // The bitmap header is 14 + 40 bytes. We could validate it but the test would fail either way. + fseek(bmp, 14 + 40, SEEK_SET); + fread(reference, sizeof(u32), w * h, bmp); + fclose(bmp); + } + else + { + error = "Unable to read screenshot: " + screenshotFilename; + free(reference); + return -1.0f; + } + + u32 errors = 0; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + errors += ComparePixel(pixels32[y * stride + x], reference[y * w + x]); + } + + free(reference); + + return (double) errors / (double) (w * h); +} \ No newline at end of file diff --git a/headless/Compare.h b/headless/Compare.h new file mode 100644 index 0000000000..f51f6c1be2 --- /dev/null +++ b/headless/Compare.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "Globals.h" + +bool CompareOutput(std::string bootFilename); +double CompareScreenshot(const u8 *pixels, int w, int h, int stride, const std::string screenshotFilename, std::string &error); \ No newline at end of file diff --git a/headless/Headless.cpp b/headless/Headless.cpp index e6ff10335b..29bfb4428d 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -13,6 +13,7 @@ #include "Log.h" #include "LogManager.h" +#include "Compare.h" #include "StubHost.h" #ifdef _WIN32 #include "WindowsHeadlessHost.h" @@ -204,17 +205,7 @@ int main(int argc, const char* argv[]) headlessHost = NULL; if (autoCompare) - { - std::string expect_filename = std::string(bootFilename).substr(strlen(bootFilename - 4)) + ".expected"; - if (File::Exists(expect_filename)) - { - // TODO: Do the compare here - } - else - { - fprintf(stderr, "Expectation file %s not found", expect_filename.c_str()); - } - } + CompareOutput(bootFilename); return 0; } diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index bc5cb35624..ecfadbd1ab 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -145,6 +145,7 @@ + NotUsing NotUsing @@ -177,6 +178,7 @@ + diff --git a/headless/Headless.vcxproj.filters b/headless/Headless.vcxproj.filters index 5982bf35db..e0debd7e7b 100644 --- a/headless/Headless.vcxproj.filters +++ b/headless/Headless.vcxproj.filters @@ -4,6 +4,7 @@ + @@ -11,5 +12,6 @@ + \ No newline at end of file diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 074d8b38fa..d30313ccb8 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "WindowsHeadlessHost.h" +#include "Compare.h" #include #include @@ -94,44 +95,34 @@ void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) // We ignore the current framebuffer parameters and just grab the full screen. const static int FRAME_WIDTH = 512; const static int FRAME_HEIGHT = 272; - u32 *pixels = (u32 *) calloc(FRAME_WIDTH * FRAME_HEIGHT, sizeof(u32)); - u32 *reference = (u32 *) calloc(FRAME_WIDTH * FRAME_HEIGHT, sizeof(u32)); + u8 *pixels = new u8[FRAME_WIDTH * FRAME_HEIGHT * 4]; // TODO: Maybe the GPU should do this? glReadBuffer(GL_FRONT); glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, pixels); - BITMAPFILEHEADER header; - BITMAPINFOHEADER infoHeader; - FILE *bmp = fopen(comparisonScreenshot.c_str(), "rb"); - if (bmp) - { - fread(&header, sizeof(header), 1, bmp); - fread(&infoHeader, sizeof(infoHeader), 1, bmp); - fread(reference, sizeof(u32), FRAME_WIDTH * FRAME_HEIGHT, bmp); - fclose(bmp); - } - else - fprintf_s(out, "Unable to read screenshot: %s\n", comparisonScreenshot.c_str()); + std::string error; + double errors = CompareScreenshot(pixels, FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH, comparisonScreenshot, error); + if (errors < 0) + fprintf_s(out, "%s\n", error.c_str()); - // TODO: Better error rate and move to headless/shared between platforms. - int errors = 0; - for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; ++i) + if (errors > 0) { - // Ignore alpha. - // TODO: Error threshold? - errors += (pixels[i] & 0xFFFFFF) != (reference[i] & 0xFFFFFF) ? 1 : 0; - } + fprintf_s(out, "Screenshot error: %f%%\n", errors * 100.0f); - if (errors != 0) - { - fprintf_s(out, "Screenshot error: %f%%\n", (float) errors * 100.0f / (float) (FRAME_WIDTH * FRAME_HEIGHT)); + // Lazy, just read in the original header to output the failed screenshot. + u8 header[14 + 40] = {0}; + FILE *bmp = fopen(comparisonScreenshot.c_str(), "rb"); + if (bmp) + { + fread(&header, sizeof(header), 1, bmp); + fclose(bmp); + } FILE *saved = fopen("__testfailure.bmp", "wb"); if (saved) { fwrite(&header, sizeof(header), 1, saved); - fwrite(&infoHeader, sizeof(infoHeader), 1, saved); fwrite(pixels, sizeof(u32), FRAME_WIDTH * FRAME_HEIGHT, saved); fclose(saved); @@ -139,8 +130,7 @@ void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) } } - free(pixels); - free(reference); + delete [] pixels; } void WindowsHeadlessHost::SetComparisonScreenshot(const std::string &filename) From 7806f9835a93500b13e2111431abc59099846c4a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 07:58:10 -0800 Subject: [PATCH 11/43] Correct a missing field from savestates in files. --- Core/HLE/sceIo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 19a3a5cf4c..2073ec4f9f 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -147,9 +147,10 @@ public: p.Do(callbackID); p.Do(callbackArg); p.Do(asyncResult); - p.Do(closePending); p.Do(pendingAsyncResult); p.Do(sectorBlockMode); + p.Do(closePending); + p.Do(info); p.Do(openMode); p.DoMarker("File"); } From 3ad565b4dfd394952406983818536250b3259c9a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 08:10:20 -0800 Subject: [PATCH 12/43] Keep filehandles open properly when loading state. --- Core/HLE/sceKernel.cpp | 1 + Core/SaveState.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index e74464b129..b31719cc08 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -157,6 +157,7 @@ void __KernelDoState(PointerWrap &p) p.DoMarker("KernelObjects"); __InterruptsDoState(p); + // Memory needs to be after kernel objects, which may free kernel memory. __KernelMemoryDoState(p); __KernelThreadingDoState(p); __KernelAlarmDoState(p); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index bf4a2a8fde..8a450081c3 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -77,9 +77,10 @@ namespace SaveState Memory::DoState(p); MemoryStick_DoState(p); currentMIPS->DoState(p); - pspFileSystem.DoState(p); HLEDoState(p); __KernelDoState(p); + // Kernel object destructors might close open files, so do the filesystem last. + pspFileSystem.DoState(p); } void Enqueue(SaveState::Operation op) From 1759bb8051446f68f3c73db1e46bebac3a2ae206 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 10:20:52 -0800 Subject: [PATCH 13/43] Use RemoveFile instead of DeleteFile in fs. Windows defines DeleteFile to DeleteFileA/W, causing confusion. --- Core/FileSystems/DirectoryFileSystem.cpp | 7 ++----- Core/FileSystems/DirectoryFileSystem.h | 2 +- Core/FileSystems/FileSystem.h | 4 ++-- Core/FileSystems/ISOFileSystem.h | 2 +- Core/FileSystems/MetaFileSystem.cpp | 4 ++-- Core/FileSystems/MetaFileSystem.h | 2 +- Core/HLE/sceIo.cpp | 3 +-- 7 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index b5ea2004bc..00a163fc5c 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -31,9 +31,6 @@ #endif -#undef DeleteFile - - #if HOST_IS_CASE_SENSITIVE static bool FixFilenameCase(const std::string &path, std::string &filename) @@ -248,7 +245,7 @@ bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string return retValue; } -bool DirectoryFileSystem::DeleteFile(const std::string &filename) { +bool DirectoryFileSystem::RemoveFile(const std::string &filename) { std::string fullName = GetLocalPath(filename); #ifdef _WIN32 bool retValue = (::DeleteFileA(fullName.c_str()) == TRUE); @@ -266,7 +263,7 @@ bool DirectoryFileSystem::DeleteFile(const std::string &filename) { fullName = GetLocalPath(fullName); #ifdef _WIN32 - retValue = (::DeleteFile(fullName.c_str()) == TRUE); + retValue = (::DeleteFileA(fullName.c_str()) == TRUE); #else retValue = (0 == unlink(fullName.c_str())); #endif diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 739a8427ad..61ee605406 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -66,7 +66,7 @@ public: bool MkDir(const std::string &dirname); bool RmDir(const std::string &dirname); bool RenameFile(const std::string &from, const std::string &to); - bool DeleteFile(const std::string &filename); + bool RemoveFile(const std::string &filename); bool GetHostPath(const std::string &inpath, std::string &outpath); private: diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index f055811ebc..d24efafeff 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -110,7 +110,7 @@ public: virtual bool MkDir(const std::string &dirname) = 0; virtual bool RmDir(const std::string &dirname) = 0; virtual bool RenameFile(const std::string &from, const std::string &to) = 0; - virtual bool DeleteFile(const std::string &filename) = 0; + virtual bool RemoveFile(const std::string &filename) = 0; virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0; }; @@ -130,7 +130,7 @@ public: virtual bool MkDir(const std::string &dirname) {return false;} virtual bool RmDir(const std::string &dirname) {return false;} virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} - virtual bool DeleteFile(const std::string &filename) {return false;} + virtual bool RemoveFile(const std::string &filename) {return false;} virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;} }; diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index ab90080829..96f8b3f39e 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -44,7 +44,7 @@ public: virtual bool MkDir(const std::string &dirname) {return false;} virtual bool RmDir(const std::string &dirname) {return false;} virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} - virtual bool DeleteFile(const std::string &filename) {return false;} + virtual bool RemoveFile(const std::string &filename) {return false;} private: struct TreeEntry diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index b72ec7f098..7c971264ed 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -363,13 +363,13 @@ bool MetaFileSystem::RenameFile(const std::string &from, const std::string &to) } } -bool MetaFileSystem::DeleteFile(const std::string &filename) +bool MetaFileSystem::RemoveFile(const std::string &filename) { std::string of; IFileSystem *system; if (MapFilePath(filename, of, &system)) { - return system->DeleteFile(of); + return system->RemoveFile(of); } else { diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index b54e153613..b6bd5d9954 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -89,7 +89,7 @@ public: virtual bool MkDir(const std::string &dirname); virtual bool RmDir(const std::string &dirname); virtual bool RenameFile(const std::string &from, const std::string &to); - virtual bool DeleteFile(const std::string &filename); + virtual bool RemoveFile(const std::string &filename); // TODO: void IoCtl(...) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 19a3a5cf4c..89659c7514 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -17,7 +17,6 @@ #ifdef _WIN32 #include -#undef DeleteFile #endif #include "../Config.h" @@ -552,7 +551,7 @@ u32 sceIoRemove(const char *filename) { if(!pspFileSystem.GetFileInfo(filename).exists) return ERROR_ERRNO_FILE_NOT_FOUND; - pspFileSystem.DeleteFile(filename); + pspFileSystem.RemoveFile(filename); return 0; } From 1e2065a280eeb1d955f67a87638bb4a484c981e1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 11:03:45 -0800 Subject: [PATCH 14/43] Fix a confusing comment for screenshots. --- headless/WindowsHeadlessHost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index d30313ccb8..5e7448ae23 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -97,7 +97,7 @@ void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) const static int FRAME_HEIGHT = 272; u8 *pixels = new u8[FRAME_WIDTH * FRAME_HEIGHT * 4]; - // TODO: Maybe the GPU should do this? + // TODO: Maybe this code should be moved into GLES_GPU. glReadBuffer(GL_FRONT); glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, pixels); From b82feed8161d7ded53e4b3cfd7c8d47348275258 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 10:35:05 -0800 Subject: [PATCH 15/43] Fix leaking VFS DirectoryAssetReaders. --- Windows/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Windows/main.cpp b/Windows/main.cpp index c875f0c7b2..b568189d07 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -191,6 +191,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin } } + VFSShutdown(); + LogManager::Shutdown(); DialogManager::DestroyAll(); timeEndPeriod(1); From 2ea113369bcc08204d1f481fad9d22c1cdc2b58d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 10:41:01 -0800 Subject: [PATCH 16/43] Fix memory leak in CSO reading. --- Core/FileSystems/BlockDevices.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index c413209c0c..7d270260f5 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -165,14 +165,17 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) //if (status != Z_OK) { ERROR_LOG(LOADER, "block %d:inflate : %s[%d]\n", blockNumber, (z.msg) ? z.msg : "error", status); + inflateEnd(&z); return 1; } int cmp_size = blockSize - z.avail_out; if (cmp_size != (int)blockSize) { ERROR_LOG(LOADER, "block %d : block size error %d != %d\n", blockNumber, cmp_size, blockSize); + inflateEnd(&z); return 1; } + inflateEnd(&z); } return true; } From db18eba388e3ddb1ecab773505548b12e272f6c3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 8 Feb 2013 13:17:47 -0800 Subject: [PATCH 17/43] Fix a crash in CSO reading (probably a bad rip.) This could happen if sce_lbn is used out of bounds, maybe? --- Core/FileSystems/BlockDevices.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index 7d270260f5..4b74a422e1 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -123,6 +123,12 @@ CISOFileBlockDevice::~CISOFileBlockDevice() bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) { + if ((u32)blockNumber >= numBlocks) + { + memset(outPtr, 0, 2048); + return false; + } + u32 idx = index[blockNumber]; u32 idx2 = index[blockNumber+1]; u8 inbuffer[4096]; //too big @@ -153,7 +159,7 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) if(inflateInit2(&z, -15) != Z_OK) { ERROR_LOG(LOADER, "deflateInit ERROR : %s\n", (z.msg) ? z.msg : "???"); - return 1; + return false; } z.avail_in = readSize; z.next_out = outPtr; @@ -173,7 +179,7 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) { ERROR_LOG(LOADER, "block %d : block size error %d != %d\n", blockNumber, cmp_size, blockSize); inflateEnd(&z); - return 1; + return false; } inflateEnd(&z); } From dc15c004e90258c428ddbed9b2a8e3e869aee21f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 00:26:46 -0800 Subject: [PATCH 18/43] Don't sleep for vsync in headless. Makes the tests run faster, timeout less. --- Core/HLE/sceDisplay.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index da021db024..f6e4c4f583 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -25,6 +25,7 @@ #include "Thread.h" #include "../Core/CoreTiming.h" +#include "../Core/CoreParameter.h" #include "../MIPS/MIPS.h" #include "../HLE/HLE.h" #include "sceAudio.h" @@ -78,7 +79,7 @@ static int hCountTotal; //unused static int vCount; static int isVblank; static bool hasSetMode; -double lastFrameTime; +static double lastFrameTime; std::vector vblankWaitingThreads; @@ -269,12 +270,11 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { host->EndFrame(); #ifdef _WIN32 - static double lastFrameTime = 0.0; // Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it. time_update(); if (lastFrameTime == 0.0) lastFrameTime = time_now_d(); - if (!GetAsyncKeyState(VK_TAB)) { + if (!GetAsyncKeyState(VK_TAB) && !PSP_CoreParameter().headLess) { while (time_now_d() < lastFrameTime + 1.0 / 60.0) { Common::SleepCurrentThread(1); time_update(); From 5afc53e42bbd373d34871a38ec6f1b756361faf5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 01:07:45 -0800 Subject: [PATCH 19/43] Consider timed out tests to have failed. --- test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test.py b/test.py index 983e47db6a..a7cf179346 100755 --- a/test.py +++ b/test.py @@ -233,6 +233,7 @@ def run_tests(test_list, args): if c.timeout: print(output) print("Test exceded limit of %d seconds." % TIMEOUT) + tests_failed.append(test) tcprint("##teamcity[testFailed name='%s' message='Test timeout']" % test) tcprint("##teamcity[testFinished name='%s']" % test) continue From 779171e8d582eb0f60d7bf4f27a52e9b18d960c2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 01:14:39 -0800 Subject: [PATCH 20/43] Run tests using jit by default. --- headless/Headless.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 29bfb4428d..a03bfe04b4 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -65,8 +65,9 @@ void printUsage(const char *progname, const char *reason) fprintf(stderr, " --screenshot=FILE compare against a screenshot\n"); } + fprintf(stderr, " -i use the interpreter\n"); fprintf(stderr, " -f use the fast interpreter\n"); - fprintf(stderr, " -j use jit (overrides -f)\n"); + fprintf(stderr, " -j use jit (default)\n"); fprintf(stderr, " -c, --compare compare with output in file.expected\n"); fprintf(stderr, "\nSee headless.txt for details.\n"); } @@ -74,7 +75,7 @@ void printUsage(const char *progname, const char *reason) int main(int argc, const char* argv[]) { bool fullLog = false; - bool useJit = false; + bool useJit = true; bool fastInterpreter = false; bool autoCompare = false; bool useGraphics = false; @@ -96,6 +97,8 @@ int main(int argc, const char* argv[]) readMount = true; else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log")) fullLog = true; + else if (!strcmp(argv[i], "-i")) + useJit = false; else if (!strcmp(argv[i], "-j")) useJit = true; else if (!strcmp(argv[i], "-f")) @@ -153,7 +156,7 @@ int main(int argc, const char* argv[]) coreParameter.fileToStart = bootFilename; coreParameter.mountIso = mountIso ? mountIso : ""; coreParameter.startPaused = false; - coreParameter.cpuCore = useJit ? CPU_JIT : (fastInterpreter ? CPU_FASTINTERPRETER : CPU_INTERPRETER); + coreParameter.cpuCore = fastInterpreter ? CPU_FASTINTERPRETER : (useJit ? CPU_JIT : CPU_INTERPRETER); coreParameter.gpuCore = headlessHost->isGLWorking() ? GPU_GLES : GPU_NULL; coreParameter.enableSound = false; coreParameter.headLess = true; From 83caa7d31efcc0e75d232e492f590b1c03c9d3ff Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 01:19:02 -0800 Subject: [PATCH 21/43] Update tests. --- pspautotests | 2 +- test.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pspautotests b/pspautotests index 4f047eb8c7..c4427bd55d 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 4f047eb8c76c6388a63a87ec72e98c64670152d5 +Subproject commit c4427bd55d57af2484f5ccd4ec1bed2bcf674395 diff --git a/test.py b/test.py index a7cf179346..2d98e03079 100755 --- a/test.py +++ b/test.py @@ -109,6 +109,12 @@ tests_good = [ "threads/semaphores/refer/refer", "threads/semaphores/signal/signal", "threads/semaphores/wait/wait", + "threads/vpl/vpl", + "threads/vpl/delete", + "threads/vpl/free", + "threads/vpl/priority", + "threads/vpl/refer", + "threads/vpl/try", "power/power", "umd/callbacks/umd", "umd/wait/wait", @@ -124,11 +130,13 @@ tests_next = [ "threads/msgpipe/msgpipe", "threads/scheduling/scheduling", "threads/threads/threads", - "threads/vpl/vpl", "threads/vtimers/vtimer", + "threads/vpl/allocate", + "threads/vpl/create", "threads/wakeup/wakeup", "gpu/simple/simple", "gpu/triangle/triangle", + "gpu/commands/basic", "hle/check_not_used_uids", "font/fonttest", "io/cwd/cwd", From 0ff30cf2391c02c02e6bf3bb2ad5da507bb80bfe Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 01:58:03 -0800 Subject: [PATCH 22/43] Turn jit back off, build server can't handle it. --- headless/Headless.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index a03bfe04b4..3cc16ae7f9 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -75,7 +75,7 @@ void printUsage(const char *progname, const char *reason) int main(int argc, const char* argv[]) { bool fullLog = false; - bool useJit = true; + bool useJit = false; bool fastInterpreter = false; bool autoCompare = false; bool useGraphics = false; From 635822cf47781516ac7bc50c956115d382115f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Sat, 9 Feb 2013 12:34:40 +0100 Subject: [PATCH 23/43] Fix unnecessary translation in GamePadDialog --- Qt/gamepaddialog.cpp | 42 +++++++++---------- Qt/gamepaddialog.h | 1 + Qt/languages/ppsspp_en.ts | 85 --------------------------------------- Qt/languages/ppsspp_pl.ts | 85 --------------------------------------- 4 files changed, 22 insertions(+), 191 deletions(-) diff --git a/Qt/gamepaddialog.cpp b/Qt/gamepaddialog.cpp index d90421a9b3..5094fc032c 100644 --- a/Qt/gamepaddialog.cpp +++ b/Qt/gamepaddialog.cpp @@ -16,26 +16,26 @@ struct GamePadInfo // Initial values are PS3 controller GamePadInfo GamepadPadMapping[] = { - {0, 14, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_X"), QT_TRANSLATE_NOOP("gamepadMapping", "Cross")}, //A - {0, 13, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_O"), QT_TRANSLATE_NOOP("gamepadMapping", "Circle")}, //B - {0, 15, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_S"), QT_TRANSLATE_NOOP("gamepadMapping", "Square")}, //X - {0, 12, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_T"), QT_TRANSLATE_NOOP("gamepadMapping", "Triangle")}, //Y - {0, 10, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_LT"), QT_TRANSLATE_NOOP("gamepadMapping", "Left Trigger")}, //LBUMPER - {0, 11, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_RT"), QT_TRANSLATE_NOOP("gamepadMapping", "Right Trigger")}, //RBUMPER - {0, 3, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Start"), QT_TRANSLATE_NOOP("gamepadMapping", "Start")}, //START - {0, 0, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Select"), QT_TRANSLATE_NOOP("gamepadMapping", "Select")}, //SELECT - {0, 4, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Up"), QT_TRANSLATE_NOOP("gamepadMapping", "Up")}, //UP - {0, 6, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Down"), QT_TRANSLATE_NOOP("gamepadMapping", "Down")}, //DOWN - {0, 7, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Left"), QT_TRANSLATE_NOOP("gamepadMapping", "Left")}, //LEFT - {0, 5, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Right"), QT_TRANSLATE_NOOP("gamepadMapping", "Right")}, //RIGHT - {0, 0, 0, QT_TRANSLATE_NOOP("gamepadMapping", "")}, //MENU (event) - {0, 16, 0, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_Home"), QT_TRANSLATE_NOOP("gamepadMapping", "Home")}, //BACK + {0, 14, 0, "Prev_X", QT_TRANSLATE_NOOP("gamepadMapping", "Cross")}, //A + {0, 13, 0, "Prev_O", QT_TRANSLATE_NOOP("gamepadMapping", "Circle")}, //B + {0, 15, 0, "Prev_S", QT_TRANSLATE_NOOP("gamepadMapping", "Square")}, //X + {0, 12, 0, "Prev_T", QT_TRANSLATE_NOOP("gamepadMapping", "Triangle")}, //Y + {0, 10, 0, "Prev_LT", QT_TRANSLATE_NOOP("gamepadMapping", "Left Trigger")}, //LBUMPER + {0, 11, 0, "Prev_RT", QT_TRANSLATE_NOOP("gamepadMapping", "Right Trigger")}, //RBUMPER + {0, 3, 0, "Prev_Start", QT_TRANSLATE_NOOP("gamepadMapping", "Start")}, //START + {0, 0, 0, "Prev_Select", QT_TRANSLATE_NOOP("gamepadMapping", "Select")}, //SELECT + {0, 4, 0, "Prev_Up", QT_TRANSLATE_NOOP("gamepadMapping", "Up")}, //UP + {0, 6, 0, "Prev_Down", QT_TRANSLATE_NOOP("gamepadMapping", "Down")}, //DOWN + {0, 7, 0, "Prev_Left", QT_TRANSLATE_NOOP("gamepadMapping", "Left")}, //LEFT + {0, 5, 0, "Prev_Right", QT_TRANSLATE_NOOP("gamepadMapping", "Right")}, //RIGHT + {0, 0, 0, ""}, //MENU (event) + {0, 16, 0, "Prev_Home", QT_TRANSLATE_NOOP("gamepadMapping", "Home")}, //BACK // Special case for analog stick - {1, 0, -1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_ALeft"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick left")}, - {1, 0, 1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_ARight"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick right")}, - {1, 1, -1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_AUp"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick up")}, - {1, 1, 1, QT_TRANSLATE_NOOP("gamepadMapping", "Prev_ADown"), QT_TRANSLATE_NOOP("gamepadMapping", "Stick bottom")} + {1, 0, -1, "Prev_ALeft", QT_TRANSLATE_NOOP("gamepadMapping", "Stick left")}, + {1, 0, 1, "Prev_ARight", QT_TRANSLATE_NOOP("gamepadMapping", "Stick right")}, + {1, 1, -1, "Prev_AUp", QT_TRANSLATE_NOOP("gamepadMapping", "Stick up")}, + {1, 1, 1, "Prev_ADown", QT_TRANSLATE_NOOP("gamepadMapping", "Stick bottom")} }; // id for mapping in config start at offset 200 to not get over key mapping @@ -65,7 +65,7 @@ GamePadDialog::GamePadDialog(InputState* state, QWidget *parent) : for(int i=0;i<18;i++) { - QLabel* labelPreview = findChild(tr(GamepadPadMapping[i].ViewLabelName.toStdString().c_str())); + QLabel* labelPreview = findChild(GamepadPadMapping[i].ViewLabelName); if(labelPreview) { labelPreview->setVisible(false); @@ -179,7 +179,7 @@ void GamePadDialog::pollJoystick() } else if(GamepadPadMapping[i].mapping_type == 2) val = SDL_JoystickGetHat(m_joystick,GamepadPadMapping[i].mapping_in); - QLabel* labelPreview = findChild(tr(GamepadPadMapping[i].ViewLabelName.toStdString().c_str())); + QLabel* labelPreview = findChild(GamepadPadMapping[i].ViewLabelName); if(labelPreview) { labelPreview->setVisible(val != 0); @@ -211,7 +211,7 @@ void GamePadDialog::pollJoystick() } else if(GamepadPadMapping[i].mapping_type == 2) val = SDL_JoystickGetHat(m_joystick,GamepadPadMapping[i].mapping_in); - QLabel* labelPreview = findChild(tr(GamepadPadMapping[i].ViewLabelName.toStdString().c_str())); + QLabel* labelPreview = findChild(GamepadPadMapping[i].ViewLabelName); if(labelPreview) { labelPreview->setVisible(val != 0); diff --git a/Qt/gamepaddialog.h b/Qt/gamepaddialog.h index 4b1fe745ae..24163731cc 100644 --- a/Qt/gamepaddialog.h +++ b/Qt/gamepaddialog.h @@ -25,6 +25,7 @@ public: void CalibNextButton(); protected: void showEvent(QShowEvent *); + void changeEvent(QEvent *); private slots: void releaseLock(); void on_refreshListBtn_clicked(); diff --git a/Qt/languages/ppsspp_en.ts b/Qt/languages/ppsspp_en.ts index 444a6cb944..9343a24500 100644 --- a/Qt/languages/ppsspp_en.ts +++ b/Qt/languages/ppsspp_en.ts @@ -545,171 +545,86 @@ gamepadMapping - - - Prev_X - - Cross - - - Prev_O - - Circle - - - Prev_S - - Square - - - Prev_T - - Triangle - - - Prev_LT - - Left Trigger - - - Prev_RT - - Right Trigger - - - Prev_Start - - Start - - - Prev_Select - - Select - - - Prev_Up - - Up - - - Prev_Down - - Down - - - Prev_Left - - Left - - - Prev_Right - - Right - - - Prev_Home - - Home - - - Prev_ALeft - - Stick left - - - Prev_ARight - - Stick right - - - Prev_AUp - - Stick up - - - Prev_ADown - - Stick bottom diff --git a/Qt/languages/ppsspp_pl.ts b/Qt/languages/ppsspp_pl.ts index a25b66f8fd..c2d398d637 100644 --- a/Qt/languages/ppsspp_pl.ts +++ b/Qt/languages/ppsspp_pl.ts @@ -545,171 +545,86 @@ gamepadMapping - - - Prev_X - - Cross Krzyżyk - - - Prev_O - - Circle Kółko - - - Prev_S - - Square Kwadrat - - - Prev_T - - Triangle Trójkąt - - - Prev_LT - - Left Trigger Lewy trigger - - - Prev_RT - - Right Trigger Prawy trigger - - - Prev_Start - - Start Start - - - Prev_Select - - Select Select - - - Prev_Up - - Up Góra - - - Prev_Down - - Down Dół - - - Prev_Left - - Left Lewo - - - Prev_Right - - Right Prawo - - - Prev_Home - - Home Klawisz Home - - - Prev_ALeft - - Stick left Lewo (analog) - - - Prev_ARight - - Stick right Prawo (analog) - - - Prev_AUp - - Stick up Góra (analog) - - - Prev_ADown - - Stick bottom From 2fcea2a7e656b0ed0b24eaa1db406bddca7d7dc7 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 9 Feb 2013 19:24:48 +0100 Subject: [PATCH 24/43] Fix mipmap-related slowdown caused by excessive texture decoding --- GPU/GLES/TextureCache.cpp | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 8edd260ddf..1c9b64628a 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -642,17 +642,27 @@ void TextureCache::SetTexture() { format = 0; } - u32 clutformat = gstate.clutformat & 3; - u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); - - int maxLevel = ((gstate.texmode >> 16) & 0x7); - const u8 *texptr = Memory::GetPointer(texaddr); u32 texhash = texptr ? MiniHash((const u32*)texptr) : 0; + u32 clutformat = gstate.clutformat & 3; + u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); + u64 cachekey = texaddr ^ texhash; if (formatUsesClut[format]) - cachekey |= (u64) clutaddr << 32; + cachekey |= (u64)clutaddr << 32; + + int maxLevel = ((gstate.texmode >> 16) & 0x7); + + // Adjust maxLevel to actually present levels.. + for (int i = 0; i <= maxLevel; i++) { + // If encountering levels pointing to nothing, adjust max level. + u32 levelTexaddr = (gstate.texaddr[i] & 0xFFFFF0) | ((gstate.texbufwidth[i] << 8) & 0x0F000000); + if (!Memory::IsValidAddress(levelTexaddr)) { + maxLevel = i - 1; + break; + } + } TexCache::iterator iter = cache.find(cachekey); if (iter != cache.end()) { @@ -749,12 +759,6 @@ void TextureCache::SetTexture() { glBindTexture(GL_TEXTURE_2D, entry.texture); for (int i = 0; i <= entry.maxLevel; i++) { - // If encountering levels pointing to nothing, adjust max level. - u32 levelTexaddr = (gstate.texaddr[i] & 0xFFFFF0) | ((gstate.texbufwidth[i] << 8) & 0x0F000000); - if (!Memory::IsValidAddress(levelTexaddr)) { - entry.maxLevel = i - 1; - break; - } LoadTextureLevel(entry, i); } @@ -799,7 +803,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) switch (entry.format) { case GE_TFMT_CLUT4: - dstFmt = getClutDestFormat((GEPaletteFormat)(gstate.clutformat & 3)); + dstFmt = getClutDestFormat((GEPaletteFormat)(entry.clutformat)); switch (entry.clutformat) { case GE_CMODE_16BIT_BGR5650: @@ -808,15 +812,15 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) { ReadClut16(clutBuf16); const u16 *clut = clutBuf16; - u32 clutSharingOff = 0;//gstate.mipmapShareClut ? 0 : level * 16; + u32 clutSharingOffset = 0; //(gstate.mipmapShareClut & 1) ? 0 : level * 16; texByteAlign = 2; if (!(gstate.texmode & 1)) { const u8 *addr = Memory::GetPointer(texaddr); for (int i = 0; i < bufw * h; i += 2) { u8 index = *addr++; - tmpTexBuf16[i + 0] = clut[GetClutIndex((index >> 0) & 0xf) + clutSharingOff]; - tmpTexBuf16[i + 1] = clut[GetClutIndex((index >> 4) & 0xf) + clutSharingOff]; + tmpTexBuf16[i + 0] = clut[GetClutIndex((index >> 0) & 0xf) + clutSharingOffset]; + tmpTexBuf16[i + 1] = clut[GetClutIndex((index >> 4) & 0xf) + clutSharingOffset]; } } else { UnswizzleFromMem(texaddr, 0, level); @@ -826,7 +830,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) u32 k, index; for (k = 0; k < 8; k++) { index = (n >> (k * 4)) & 0xf; - tmpTexBuf16[i + k] = clut[GetClutIndex(index) + clutSharingOff]; + tmpTexBuf16[i + k] = clut[GetClutIndex(index) + clutSharingOffset]; } } } From 4868b5041c2851a48ddac980c7daccbefff48a89 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 9 Feb 2013 20:21:10 +0100 Subject: [PATCH 25/43] Fall back to glGenerateMipmap on OpenGL ES 2.0 for now, explanation in comments. Enable 4x aniso by default, just because. This might be turned into an option later. --- GPU/GLES/DisplayListInterpreter.cpp | 1 + GPU/GLES/TextureCache.cpp | 44 ++++++++++------------------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index efaa5f5240..dc93c877c8 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -677,6 +677,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_TEXSIZE0: gstate_c.curTextureWidth = 1 << (gstate.texsize[0] & 0xf); gstate_c.curTextureHeight = 1 << ((gstate.texsize[0]>>8) & 0xf); + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); //fall thru - ignoring the mipmap sizes for now case GE_CMD_TEXSIZE1: case GE_CMD_TEXSIZE2: diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 1c9b64628a..ba35f533f8 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -368,12 +368,6 @@ static const GLuint MagFiltGL[2] = { #define GL_TEXTURE_LOD_BIAS 0x8501 #endif -#ifndef GL_TEXTURE_MAX_LOD -#define GL_TEXTURE_MAX_LOD 0x813B -#endif - - - // This should not have to be done per texture! OpenGL is silly yo // TODO: Dirty-check this against the current texture. void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { @@ -758,15 +752,26 @@ void TextureCache::SetTexture() { glGenTextures(1, &entry.texture); glBindTexture(GL_TEXTURE_2D, entry.texture); +#ifdef USING_GLES2 + // GLES2 doesn't have support for a "Max lod" which is critical as PSP games often + // don't specify mips all the way down. As a result, we either need to manually generate + // the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not + // be as good quality as the game's own (might even be better in some cases though). + + // For now, I choose to use autogen mips on GLES2 and the game's own on other platforms. + // As is usual, GLES3 will solve this problem nicely but wide distribution of that is + // years away. + LoadTextureLevel(entry, 0); + if (entry.maxLevel > 0) + glGenerateMipmap(GL_TEXTURE_2D); +#else for (int i = 0; i <= entry.maxLevel; i++) { LoadTextureLevel(entry, i); } - -#ifndef USING_GLES2 - // See horrifying hack at the bottom of LoadTextureLevel! glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, entry.maxLevel); -#endif glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, entry.maxLevel); +#endif + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4); UpdateSamplingParams(entry, true); @@ -1033,23 +1038,4 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components, dstFmt, finalBuf); - -#ifdef USING_GLES2 - // ARGH! OpenGL ES does not support max texture level! - // Let's do a HORRIBLE hack for now and re-specify the last level, but with changed dimensions. - // Will at least give us sort of the right colors and hopefully it'll be too blurry anyway. - // Later I will add proper downsampling of the bottom level. - // TEXTURE_MAX_LOD should ensure that we never get to see these anyway. - if (level == entry.maxLevel) { - while (w >= 2 || h >= 2) { - w /= 2; - h /= 2; - if (w == 0) w = 1; - if (h == 0) h = 1; - ++level; - INFO_LOG(HLE, "Specifying extra texture level %i : %ix%i", level, w, h); - glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components, dstFmt, finalBuf); - } - } -#endif } \ No newline at end of file From ca347da99ce1db355e586891fe1e23b99583aadf Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 9 Feb 2013 20:53:32 +0100 Subject: [PATCH 26/43] Depth range should not be applied in through mode. Fixes sky in Wipeout Pure. --- GPU/GLES/DisplayListInterpreter.cpp | 8 -------- GPU/GLES/StateMapping.cpp | 17 +++++++---------- GPU/GPUState.h | 4 ++-- GPU/GeDisasm.cpp | 4 ++-- native | 2 +- 5 files changed, 12 insertions(+), 23 deletions(-) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index dc93c877c8..3be5d5554b 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -798,16 +798,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_VIEWPORTY1: case GE_CMD_VIEWPORTX2: case GE_CMD_VIEWPORTY2: - break; - case GE_CMD_VIEWPORTZ1: - gstate_c.zScale = getFloat24(data) / 65535.f; - break; - case GE_CMD_VIEWPORTZ2: - gstate_c.zOff = getFloat24(data) / 65535.f; - break; - case GE_CMD_LIGHTENABLE0: case GE_CMD_LIGHTENABLE1: case GE_CMD_LIGHTENABLE2: diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 5d4a952404..65aabfdd81 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -208,10 +208,6 @@ void TransformDrawEngine::ApplyDrawState(int prim) { bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled(); glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE); - - float depthRangeMin = gstate_c.zOff - gstate_c.zScale; - float depthRangeMax = gstate_c.zOff + gstate_c.zScale; - glstate.depthRange.set(depthRangeMin, depthRangeMax); } void UpdateViewportAndProjection() { @@ -241,14 +237,13 @@ void UpdateViewportAndProjection() { if (throughmode) { // No viewport transform here. Let's experiment with using region. glstate.viewport.set((0 + regionX1) * renderWidthFactor, (0 - regionY1) * renderHeightFactor, (regionX2 - regionX1) * renderWidthFactor, (regionY2 - regionY1) * renderHeightFactor); + glstate.depthRange.set(1.0, 0.0); } else { // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. float vpXa = getFloat24(gstate.viewportx1); float vpXb = getFloat24(gstate.viewportx2); float vpYa = getFloat24(gstate.viewporty1); float vpYb = getFloat24(gstate.viewporty2); - float vpZa = getFloat24(gstate.viewportz1); // / 65536.0f should map it to OpenGL's 0.0-1.0 Z range - float vpZb = getFloat24(gstate.viewportz2); // / 65536.0f // The viewport transform appears to go like this: // Xscreen = -offsetX + vpXb + vpXa * Xview @@ -264,10 +259,6 @@ void UpdateViewportAndProjection() { float vpWidth = fabsf(gstate_c.vpWidth); float vpHeight = fabsf(gstate_c.vpHeight); - // TODO: These two should feed into glDepthRange somehow. - float vpZ0 = (vpZb - vpZa) / 65536.0f; - float vpZ1 = (vpZa * 2) / 65536.0f; - vpX0 *= renderWidthFactor; vpY0 *= renderHeightFactor; vpWidth *= renderWidthFactor; @@ -278,5 +269,11 @@ void UpdateViewportAndProjection() { glstate.viewport.set(vpX0, vpY0, vpWidth, vpHeight); // Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh. // shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + + float zScale = getFloat24(gstate.viewportz1) / 65535.f; + float zOff = getFloat24(gstate.viewportz2) / 65535.f; + float depthRangeMin = zOff - zScale; + float depthRangeMax = zOff + zScale; + glstate.depthRange.set(depthRangeMin, depthRangeMax); } } diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 4710856152..84d9d8dcea 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -236,8 +236,8 @@ struct GPUStateCache bool textureChanged; - float uScale,vScale,zScale; - float uOff,vOff,zOff; + float uScale,vScale; + float uOff,vOff; float zMin, zMax; float lightpos[4][3]; float lightdir[4][3]; diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index 49d0c735f4..c75dc1ba93 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -521,13 +521,13 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer) { case GE_CMD_VIEWPORTZ1: { float zScale = getFloat24(data) / 65535.f; - sprintf(buffer, "Z scale: %f", zScale); + sprintf(buffer, "Viewport Z scale: %f", zScale); } break; case GE_CMD_VIEWPORTZ2: { float zOff = getFloat24(data) / 65535.f; - sprintf(buffer, "Z pos: %f", zOff); + sprintf(buffer, "Viewport Z pos: %f", zOff); } break; diff --git a/native b/native index f22ad17d40..3caced8524 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit f22ad17d40c00d9a60bd21f53820012b302d7559 +Subproject commit 3caced8524c06cabcf968942a7780d80337de7bf From 5a43f3e0ff9446debb5da385fba2386bc84adc70 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 9 Feb 2013 21:25:15 +0100 Subject: [PATCH 27/43] ZWRITEDISABLE should flush drawbuffer. Fixes road glitches in Ridge Racer and MotoGP. --- GPU/GLES/DisplayListInterpreter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index 9f6b4b8f50..ff1b55d555 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -110,6 +110,7 @@ static const int flushOnChangedBeforeCommandList[] = { GE_CMD_TEXFORMAT, GE_CMD_TEXWRAP, GE_CMD_ZTESTENABLE, + GE_CMD_ZWRITEDISABLE, GE_CMD_STENCILTESTENABLE, GE_CMD_STENCILOP, GE_CMD_STENCILTEST, From db3f01044d794b5902b7d680821b8b84974f7891 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 9 Feb 2013 21:32:02 +0100 Subject: [PATCH 28/43] Turn down texturecache logging a bit. Fix unfinished optimization. --- GPU/GLES/TextureCache.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 7714961d2b..57893e1551 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -114,19 +114,17 @@ void TextureCache::InvalidateAll(bool force) { TextureCache::TexCacheEntry *TextureCache::GetEntryAt(u32 texaddr) { // If no CLUT, as in framebuffer textures, cache key is simply texaddr. auto iter = cache.find(texaddr); - for (auto entry = cache.begin(); entry != cache.end(); ++entry) { - if (entry->second.addr == texaddr) { - return &entry->second; - } - } - return 0; + if (iter != cache.end() && iter->second.addr == texaddr) + return &iter->second; + else + return 0; } void TextureCache::NotifyFramebuffer(u32 address, FBO *fbo) { // Must be in VRAM so | 0x04000000 it is. TexCacheEntry *entry = GetEntryAt(address | 0x04000000); if (entry) { - INFO_LOG(HLE, "Render to texture detected at %08x!", address); + // INFO_LOG(HLE, "Render to texture detected at %08x!", address); if (!entry->fbo) entry->fbo = fbo; // TODO: Delete the original non-fbo texture too. @@ -1080,7 +1078,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) //glPixelStorei(GL_PACK_ROW_LENGTH, bufw); glPixelStorei(GL_PACK_ALIGNMENT, texByteAlign); - INFO_LOG(HLE, "Creating texture level %i/%i from %08x: %i x %i (stride: %i). fmt: %i", level, entry.maxLevel, texaddr, w, h, bufw, entry.format); + // INFO_LOG(G3D, "Creating texture level %i/%i from %08x: %i x %i (stride: %i). fmt: %i", level, entry.maxLevel, texaddr, w, h, bufw, entry.format); GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components, dstFmt, finalBuf); From 411c711daceeea0d84f1675299700f4573f54c0e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 02:17:19 -0800 Subject: [PATCH 29/43] Initial sceKernelRotateThreadReadyQueue(). Might not be right but it's closer. Makes games that call this func much faster. --- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelThread.cpp | 25 +++++++++++++++++++++++-- Core/HLE/sceKernelThread.h | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index b31719cc08..04e5a8368e 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -590,7 +590,7 @@ const HLEFunction ThreadManForUser[] = {0x75156e8f,sceKernelResumeThread,"sceKernelResumeThread"}, {0x3ad58b8c,&WrapU_V,"sceKernelSuspendDispatchThread"}, {0x27e22ec2,&WrapU_U,"sceKernelResumeDispatchThread"}, - {0x912354a7,sceKernelRotateThreadReadyQueue,"sceKernelRotateThreadReadyQueue"}, + {0x912354a7,&WrapI_I,"sceKernelRotateThreadReadyQueue"}, {0x9ACE131E,sceKernelSleepThread,"sceKernelSleepThread"}, {0x82826f70,sceKernelSleepThreadCB,"sceKernelSleepThreadCB"}, {0xF475845D,&WrapI_IUU,"sceKernelStartThread"}, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 293bab900e..93e8f8b794 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1600,10 +1600,31 @@ u32 sceKernelResumeDispatchThread(u32 suspended) return oldDispatchSuspended; } -void sceKernelRotateThreadReadyQueue() +int sceKernelRotateThreadReadyQueue(int priority) { DEBUG_LOG(HLE,"sceKernelRotateThreadReadyQueue : rescheduling"); - hleReSchedule("rotatethreadreadyqueue"); + + // TODO: Does it try better-priority threads? Is 0 special? + if (!threadReadyQueue[priority].empty()) + { + Thread *cur = __GetCurrentThread(); + // TODO: Who gets switched to with currentThread-priority? Next or next-next? + if (cur->nt.currentPriority == priority) + __KernelChangeReadyState(currentThread, true); + + size_t readySize = threadReadyQueue[priority].size(); + if (readySize > 1) + { + SceUID first = threadReadyQueue[priority][0]; + memmove(&threadReadyQueue[priority][0], &threadReadyQueue[priority][1], (readySize - 1) * sizeof(SceUID)); + threadReadyQueue[priority][readySize - 1] = first; + } + hleReSchedule("rotatethreadreadyqueue"); + } + // TODO: Does it reschedule in other cases? + + // TODO: Any way to get a different return? + return 0; } int sceKernelDeleteThread(int threadHandle) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 86a158b5a4..1000f9597a 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -46,7 +46,7 @@ u32 sceKernelReferThreadStatus(u32 uid, u32 statusPtr); u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr); int sceKernelReleaseWaitThread(SceUID threadID); void sceKernelChangeCurrentThreadAttr(); -void sceKernelRotateThreadReadyQueue(); +int sceKernelRotateThreadReadyQueue(int priority); void sceKernelCheckThreadStack(); void sceKernelSuspendThread(); void sceKernelResumeThread(); From b8ca8a44aa82101c5f53fb060f661d9933035df4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 14:27:16 -0800 Subject: [PATCH 30/43] Fix completely wrong thread status switching. Wow, kinda surprised this even worked... But now things that use sceKernelRotateThreadReadyQueue are broken again. --- Core/HLE/sceKernelThread.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 93e8f8b794..afb8dfd4f7 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1182,10 +1182,6 @@ Thread *__KernelNextThread() { void __KernelReSchedule(const char *reason) { - // TODO: Not sure if this is correct? - if (__GetCurrentThread() && __GetCurrentThread()->isRunning()) - __KernelChangeReadyState(currentThread, true); - // cancel rescheduling when in interrupt or callback, otherwise everything will be fucked up if (__IsInInterrupt() || __KernelInCallback()) { @@ -1208,6 +1204,10 @@ void __KernelReSchedule(const char *reason) return; } + // TODO: Not sure if this is correct? Probably should remove. + if (__GetCurrentThread() && __GetCurrentThread()->isRunning()) + __KernelChangeReadyState(currentThread, true); + retry: Thread *nextThread = __KernelNextThread(); @@ -1439,6 +1439,8 @@ int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) threadToStartID,argSize,argBlockPtr); __KernelResetThread(startThread); + if (currentThread) + __KernelChangeReadyState(currentThread, true); __KernelChangeReadyState(startThread, threadToStartID, true, true); u32 sp = startThread->context.r[MIPS_REG_SP]; @@ -1602,7 +1604,7 @@ u32 sceKernelResumeDispatchThread(u32 suspended) int sceKernelRotateThreadReadyQueue(int priority) { - DEBUG_LOG(HLE,"sceKernelRotateThreadReadyQueue : rescheduling"); + ERROR_LOG(HLE, "sceKernelRotateThreadReadyQueue(%x)", priority); // TODO: Does it try better-priority threads? Is 0 special? if (!threadReadyQueue[priority].empty()) @@ -2278,15 +2280,15 @@ void __KernelSwitchContext(Thread *target, const char *reason) oldName = cur->GetName(); if (cur->isRunning()) - { - __KernelChangeReadyState(cur, oldUID, false); - cur->nt.status = (cur->nt.status | THREADSTATUS_READY) & ~THREADSTATUS_RUNNING; - } + __KernelChangeReadyState(cur, oldUID, true); } currentThread = target->GetUID(); - if (target && target->isRunning()) - __KernelChangeReadyState(target, currentThread, true); + if (target) + { + __KernelChangeReadyState(target, currentThread, false); + target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + } __KernelLoadContext(&target->context); From ea112fd9a60a3ee4d09f12def0c4fe094b94bb61 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 15:16:37 -0800 Subject: [PATCH 31/43] Start the root thread as running, properly. --- Core/HLE/sceKernelThread.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index afb8dfd4f7..c799308523 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -446,7 +446,7 @@ int g_inCbCount = 0; // Normally, the same as currentThread. In an interrupt, remembers the callback's thread id. SceUID currentCallbackThreadID = 0; int readyCallbacksCount = 0; -SceUID currentThread; +SceUID currentThread = 0; u32 idleThreadHackAddr; u32 threadReturnHackAddr; u32 cbReturnHackAddr; @@ -678,9 +678,8 @@ void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready, bool } else threadReadyQueue[prio].push_back(threadID); + thread->nt.status = THREADSTATUS_READY; } - - thread->nt.status = THREADSTATUS_READY; } void __KernelChangeReadyState(SceUID threadID, bool ready) @@ -1361,8 +1360,11 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr Thread *thread = __KernelCreateThread(id, moduleID, "root", currentMIPS->pc, prio, stacksize, attr); __KernelResetThread(thread); + Thread *prevThread = __GetCurrentThread(); + if (prevThread && prevThread->isRunning()) + __KernelChangeReadyState(currentThread, true); currentThread = id; - __KernelChangeReadyState(thread, id, true); // do not schedule + thread->nt.status = THREADSTATUS_RUNNING; // do not schedule strcpy(thread->nt.name, "root"); From ddc93df61e89cef67feae7e412ddfe873ee71f60 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 15:30:22 -0800 Subject: [PATCH 32/43] Keep running threads at the top of the list. There we go, this seems more right. Switched to a std::list too. --- Core/HLE/sceKernelThread.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index c799308523..11aa31b256 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -453,9 +453,10 @@ u32 cbReturnHackAddr; u32 intReturnHackAddr; std::vector threadEndListeners; -typedef std::vector ThreadList; // Lists all thread ids that aren't deleted/etc. -ThreadList threadqueue; +std::vector threadqueue; + +typedef std::list ThreadList; // Lists only ready thread ids. std::map threadReadyQueue; @@ -664,18 +665,12 @@ void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready, bool if (thread->isReady()) { if (!ready) - threadReadyQueue[prio].erase(std::remove(threadReadyQueue[prio].begin(), threadReadyQueue[prio].end(), threadID), threadReadyQueue[prio].end()); + threadReadyQueue[prio].remove(threadID); } else if (ready) { - if (atStart) - { - size_t oldSize = threadReadyQueue[prio].size(); - threadReadyQueue[prio].resize(oldSize + 1); - if (oldSize > 0) - memmove(&threadReadyQueue[prio][1], &threadReadyQueue[prio][0], oldSize * sizeof(SceUID)); - threadReadyQueue[prio][0] = threadID; - } + if (atStart || thread->isRunning()) + threadReadyQueue[prio].push_front(threadID); else threadReadyQueue[prio].push_back(threadID); thread->nt.status = THREADSTATUS_READY; @@ -1129,7 +1124,7 @@ void __KernelRemoveFromThreadQueue(SceUID threadID) { int prio = __KernelGetThreadPrio(threadID); if (prio != 0) - threadReadyQueue[prio].erase(std::remove(threadReadyQueue[prio].begin(), threadReadyQueue[prio].end(), threadID), threadReadyQueue[prio].end()); + threadReadyQueue[prio].remove(threadID); threadqueue.erase(std::remove(threadqueue.begin(), threadqueue.end(), threadID), threadqueue.end()); } @@ -1167,7 +1162,7 @@ Thread *__KernelNextThread() { { if (!it->second.empty()) { - bestThread = it->second[0]; + bestThread = it->second.front(); break; } } @@ -1616,12 +1611,11 @@ int sceKernelRotateThreadReadyQueue(int priority) if (cur->nt.currentPriority == priority) __KernelChangeReadyState(currentThread, true); - size_t readySize = threadReadyQueue[priority].size(); - if (readySize > 1) + if (threadReadyQueue[priority].size() > 1) { - SceUID first = threadReadyQueue[priority][0]; - memmove(&threadReadyQueue[priority][0], &threadReadyQueue[priority][1], (readySize - 1) * sizeof(SceUID)); - threadReadyQueue[priority][readySize - 1] = first; + SceUID first = threadReadyQueue[priority].front(); + threadReadyQueue[priority].pop_front(); + threadReadyQueue[priority].push_back(first); } hleReSchedule("rotatethreadreadyqueue"); } From 161f2d712ec7dda515b1d8b9f4c210d4f9ea4ef8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 15:54:13 -0800 Subject: [PATCH 33/43] sceKernelRotateThreadReadyQueue(0) means current. --- Core/HLE/sceKernelThread.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 11aa31b256..41a0aef0fc 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1601,27 +1601,33 @@ u32 sceKernelResumeDispatchThread(u32 suspended) int sceKernelRotateThreadReadyQueue(int priority) { - ERROR_LOG(HLE, "sceKernelRotateThreadReadyQueue(%x)", priority); + DEBUG_LOG(HLE, "sceKernelRotateThreadReadyQueue(%x)", priority); + + Thread *cur = __GetCurrentThread(); + + // 0 is special, it means "my current priority." + if (priority == 0) + priority = cur->nt.currentPriority; - // TODO: Does it try better-priority threads? Is 0 special? if (!threadReadyQueue[priority].empty()) { - Thread *cur = __GetCurrentThread(); - // TODO: Who gets switched to with currentThread-priority? Next or next-next? + // In other words, yield to everyone else. if (cur->nt.currentPriority == priority) - __KernelChangeReadyState(currentThread, true); - - if (threadReadyQueue[priority].size() > 1) + { + threadReadyQueue[priority].push_back(currentThread); + cur->nt.status = THREADSTATUS_READY; + } + // Yield the next thread of this priority to all other threads of same priority. + else if (threadReadyQueue[priority].size() > 1) { SceUID first = threadReadyQueue[priority].front(); threadReadyQueue[priority].pop_front(); threadReadyQueue[priority].push_back(first); } + hleReSchedule("rotatethreadreadyqueue"); } - // TODO: Does it reschedule in other cases? - // TODO: Any way to get a different return? return 0; } From 74c2769ada02d6f6de4a581acab949b921a22806 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 17:18:01 -0800 Subject: [PATCH 34/43] Don't use an f suffix in GL shaders. Error found in Hexyz Force. --- GPU/GLES/VertexShaderGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index d6cc3770c9..f9da12ed74 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -375,7 +375,7 @@ void GenerateVertexShader(int prim, char *buffer) { WRITE(p, " vec3 temp_tc = a_position.xyz;\n"); break; case 1: // Use unscaled UV as source - WRITE(p, " vec3 temp_tc = vec3(a_texcoord.xy * 2.0f, 0.0);\n"); + WRITE(p, " vec3 temp_tc = vec3(a_texcoord.xy * 2.0, 0.0);\n"); break; case 2: // Use normalized transformed normal as source WRITE(p, " vec3 temp_tc = normalize(a_normal);\n"); From 0ff0b3f57d2610ca7bfb4e604b272bc80e571e84 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 18:56:34 -0800 Subject: [PATCH 35/43] Return an error for invalid priority. --- Core/HLE/sceKernelThread.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 41a0aef0fc..30f303b93c 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1609,6 +1609,9 @@ int sceKernelRotateThreadReadyQueue(int priority) if (priority == 0) priority = cur->nt.currentPriority; + if (priority <= 0x07 || priority > 0x77) + return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; + if (!threadReadyQueue[priority].empty()) { // In other words, yield to everyone else. From a5ba6821b794a658a606371206f8674b5cc3204b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 19:02:38 -0800 Subject: [PATCH 36/43] Only switch to a started thread if better priority. --- Core/HLE/sceKernelThread.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 30f303b93c..bda308d7a9 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -658,7 +658,7 @@ void __KernelFireThreadEnd(SceUID threadID) } // TODO: Use __KernelChangeThreadState instead? It has other affects... -void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready, bool atStart = false) +void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready) { int prio = thread->nt.currentPriority; @@ -669,7 +669,7 @@ void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready, bool } else if (ready) { - if (atStart || thread->isRunning()) + if (thread->isRunning()) threadReadyQueue[prio].push_front(threadID); else threadReadyQueue[prio].push_back(threadID); @@ -1436,9 +1436,6 @@ int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) threadToStartID,argSize,argBlockPtr); __KernelResetThread(startThread); - if (currentThread) - __KernelChangeReadyState(currentThread, true); - __KernelChangeReadyState(startThread, threadToStartID, true, true); u32 sp = startThread->context.r[MIPS_REG_SP]; if (argBlockPtr && argSize > 0) @@ -1461,7 +1458,14 @@ int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) WARN_LOG(HLE,"sceKernelStartThread : had NULL arg"); } - hleReSchedule("thread started"); + Thread *cur = __GetCurrentThread(); + // Smaller is better for priority. Only switch if the new thread is better. + if (cur && cur->nt.currentPriority > startThread->nt.currentPriority) + { + __KernelChangeReadyState(currentThread, true); + hleReSchedule("thread started"); + } + __KernelChangeReadyState(startThread, threadToStartID, true); return 0; } else From 5f2ec4520342540923654443e9e3fb2a56ee5ea4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 20:40:06 -0800 Subject: [PATCH 37/43] sceIoDread() should return 1 when there are more. Before it was returning > 1 if there were many more, which the PSP did not do. --- Core/HLE/sceIo.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 69c24f6c43..25341ea1db 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -1031,26 +1031,27 @@ u32 sceIoDread(int id, u32 dirent_addr) { u32 error; DirListing *dir = kernelObjects.Get(id, error); if (dir) { + SceIoDirEnt *entry = (SceIoDirEnt*) Memory::GetPointer(dirent_addr); + if (dir->index == (int) dir->listing.size()) { DEBUG_LOG(HLE, "sceIoDread( %d %08x ) - end of the line", id, dirent_addr); + entry->d_name[0] = '\0'; return 0; } PSPFileInfo &info = dir->listing[dir->index]; - - SceIoDirEnt *entry = (SceIoDirEnt*) Memory::GetPointer(dirent_addr); - __IoGetStat(&entry->d_stat, info); strncpy(entry->d_name, info.name.c_str(), 256); + entry->d_name[255] = '\0'; entry->d_private = 0xC0DEBABE; DEBUG_LOG(HLE, "sceIoDread( %d %08x ) = %s", id, dirent_addr, entry->d_name); dir->index++; - return (u32)(dir->listing.size() - dir->index + 1); + return 1; } else { DEBUG_LOG(HLE, "sceIoDread - invalid listing %i, error %08x", id, error); - return -1; // TODO + return SCE_KERNEL_ERROR_BADF; } } From fbfc1b7f102275f3353343bd30621cf6c365c3a4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 21:26:55 -0800 Subject: [PATCH 38/43] Correctly read ISO 9660 directory entries. Before, we would often duplicate the last entry in a directory. --- Core/FileSystems/ISOFileSystem.cpp | 120 ++++++++++++++--------------- 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index 2a2403cff5..7e81411d4a 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -178,80 +178,74 @@ ISOFileSystem::~ISOFileSystem() void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root) { - u8 buffer[2048]; - int offset = 0; - u32 secnum = startsector; - - u8 theSector[2048]; - blockDevice->ReadBlock(secnum, theSector); - - while (secnum < (dirsize/2048 + startsector)) + for (u32 secnum = startsector, endsector = dirsize/2048 + startsector; secnum < endsector; ++secnum) { - DirectoryEntry &dir = *((DirectoryEntry *)buffer); - u8 sz = theSector[offset]; - if (sz == 0) // NOT the correct way - goto nextblock; //done + u8 theSector[2048]; + blockDevice->ReadBlock(secnum, theSector); - memcpy(&dir, theSector + offset, sz); - - buffer[2047]=0; - offset += dir.size; - if (offset >= 2048) + for (int offset = 0; offset < 2048; ) { -nextblock: - offset=0; - secnum++; - blockDevice->ReadBlock(secnum, theSector); - memcpy(&dir, theSector + offset, sz); - } - bool isFile = (dir.flags & 2) ? false : true; + DirectoryEntry &dir = *(DirectoryEntry *)&theSector[offset]; + u8 sz = theSector[offset]; - int fnLength = dir.identifierLength; + // Nothing left in this sector. There might be more in the next one. + if (sz == 0) + break; - char name[256]; - for (int i = 0; i < fnLength; i++) - name[i] = buffer[33+i] ? buffer[33+i] : '.'; - name[fnLength] = '\0'; - - bool relative = false; - - if (!strcmp(name, ".")) // "." record - { - relative = true; - } - - if (strlen(name) == 1 && name[0] == '\x01') // ".." record - { - strcpy(name,".."); - relative = true; - } - - TreeEntry *e = new TreeEntry; - e->name = name; - e->size = dir.dataLengthLE; - e->startingPosition = dir.firstDataSectorLE * 2048; - e->isDirectory = !isFile; - e->flags = dir.flags; - e->isBlockSectorMode = false; - e->parent = root; - - // Let's not excessively spam the log - I commented this line out. - //DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", e->isDirectory?"D":"F", name, dir.firstDataSectorLE, e->startingPosition, e->startingPosition); - - if (e->isDirectory && !relative) - { - if (dir.firstDataSectorLE == startsector) + const int IDENTIFIER_OFFSET = 33; + if (offset + IDENTIFIER_OFFSET + dir.identifierLength > 2048) { - ERROR_LOG(FILESYS, "WARNING: Appear to have a recursive file system, breaking recursion"); + ERROR_LOG(FILESYS, "Directory entry crosses sectors, corrupt iso?"); + break; + } + + offset += dir.size; + + bool isFile = (dir.flags & 2) ? false : true; + bool relative; + int fnLength = dir.identifierLength; + + TreeEntry *e = new TreeEntry(); + if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.')) + { + e->name = "."; + relative = true; + } + else if (dir.identifierLength == 1 && dir.firstIdChar == '\x01') + { + e->name = ".."; + relative = true; } else { - ReadDirectory(dir.firstDataSectorLE, dir.dataLengthLE, e); + e->name = std::string((char *)&dir.firstIdChar, dir.identifierLength); + relative = false; } - } - root->children.push_back(e); - } + e->size = dir.dataLengthLE; + e->startingPosition = dir.firstDataSectorLE * 2048; + e->isDirectory = !isFile; + e->flags = dir.flags; + e->isBlockSectorMode = false; + e->parent = root; + + // Let's not excessively spam the log - I commented this line out. + //DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", e->isDirectory?"D":"F", e->name.c_str(), dir.firstDataSectorLE, e->startingPosition, e->startingPosition); + + if (e->isDirectory && !relative) + { + if (dir.firstDataSectorLE == startsector) + { + ERROR_LOG(FILESYS, "WARNING: Appear to have a recursive file system, breaking recursion"); + } + else + { + ReadDirectory(dir.firstDataSectorLE, dir.dataLengthLE, e); + } + } + root->children.push_back(e); + } + } } ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path, bool catchError) From 71c85ccf33f03a72e0c86c8e8fbe8f286122ef88 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 23:04:39 -0800 Subject: [PATCH 39/43] In jit slowmem, verify actual address. Oops, it could crash if it was near the boundary. Well, it still could if it were very near, but that's rare. --- Core/MIPS/x86/Jit.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 5efaa0c7b8..8574d454ac 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -413,9 +413,9 @@ OpArg Jit::JitSafeMem::PrepareMemoryOpArg() if (!g_Config.bFastMemory) { // Is it in physical ram? - jit_->CMP(32, R(xaddr_), Imm32(PSP_GetKernelMemoryBase())); + jit_->CMP(32, R(xaddr_), Imm32(PSP_GetKernelMemoryBase() - offset_)); tooLow_ = jit_->J_CC(CC_L); - jit_->CMP(32, R(xaddr_), Imm32(PSP_GetUserMemoryEnd())); + jit_->CMP(32, R(xaddr_), Imm32(PSP_GetUserMemoryEnd() - offset_)); tooHigh_ = jit_->J_CC(CC_GE); // We may need to jump back up here. @@ -448,9 +448,9 @@ void Jit::JitSafeMem::PrepareSlowAccess() jit_->SetJumpTarget(tooHigh_); // Might also be the scratchpad. - jit_->CMP(32, R(xaddr_), Imm32(PSP_GetScratchpadMemoryBase())); + jit_->CMP(32, R(xaddr_), Imm32(PSP_GetScratchpadMemoryBase() - offset_)); FixupBranch tooLow = jit_->J_CC(CC_L); - jit_->CMP(32, R(xaddr_), Imm32(PSP_GetScratchpadMemoryEnd())); + jit_->CMP(32, R(xaddr_), Imm32(PSP_GetScratchpadMemoryEnd() - offset_)); jit_->J_CC(CC_L, safe_); jit_->SetJumpTarget(tooLow); } From eb84c2f00a76f5e6aa96650dd60ff4e9223d0b5f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Feb 2013 23:11:26 -0800 Subject: [PATCH 40/43] Validate jumps in jit slowmem mode. This makes it easier to see what is going on in the emulator debugger. --- Core/MIPS/x86/Jit.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 8574d454ac..6f26a4bc22 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -288,7 +288,31 @@ void Jit::WriteExitDestInEAX() // TODO: Some wasted potential, dispatcher will always read this back into EAX. MOV(32, M(&mips_->pc), R(EAX)); WriteDowncount(); - JMP(asm_.dispatcher, true); + + // Validate the jump to avoid a crash? + if (!g_Config.bFastMemory) + { + CMP(32, R(EAX), Imm32(PSP_GetKernelMemoryBase())); + FixupBranch tooLow = J_CC(CC_L); + CMP(32, R(EAX), Imm32(PSP_GetUserMemoryEnd())); + FixupBranch tooHigh = J_CC(CC_GE); + + JMP(asm_.dispatcher, true); + + SetJumpTarget(tooLow); + SetJumpTarget(tooHigh); + + ABI_CallFunctionA(thunks.ProtectFunction((void *) Memory::GetPointer, 1), R(EAX)); + CMP(32, R(EAX), Imm32(0)); + J_CC(CC_NE, asm_.dispatcher, true); + + // TODO: "Ignore" this so other threads can continue? + if (g_Config.bIgnoreBadMemAccess) + MOV(32, M((void*)&coreState), Imm32(CORE_ERROR)); + JMP(asm_.dispatcherCheckCoreState, true); + } + else + JMP(asm_.dispatcher, true); } void Jit::WriteSyscallExit() From 430139b12a9f3563f19e85abc2d31fffccfadbde Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 5 Feb 2013 00:44:21 -0800 Subject: [PATCH 41/43] Minimal revert of the GE-related interrupt changes. Keep most of them, just revert using getList() which isn't cutting it right now. Fixes #595. --- Core/HLE/sceGe.cpp | 13 +++++++++---- Core/HLE/sceGe.h | 2 +- GPU/GLES/DisplayListInterpreter.cpp | 4 ++-- GPU/Null/NullGpu.cpp | 4 ++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index 91c687d65d..a8de87bf08 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -33,6 +33,8 @@ struct GeInterruptData { int listid; u32 pc; + u32 subIntrBase; + u16 subIntrToken; }; static std::list ge_pending_cb; @@ -50,20 +52,21 @@ public: if (dl == NULL) { WARN_LOG(HLE, "Unable to run GE interrupt: list doesn't exist: %d", intrdata.listid); - return false; + // TODO: Use dl instead of just saving everything instead? + //return false; } gpu->InterruptStart(); u32 cmd = Memory::ReadUnchecked_U32(intrdata.pc) >> 24; - int subintr = dl->subIntrBase | (cmd == GE_CMD_FINISH ? PSP_GE_SUBINTR_FINISH : PSP_GE_SUBINTR_SIGNAL); + int subintr = intrdata.subIntrBase | (cmd == GE_CMD_FINISH ? PSP_GE_SUBINTR_FINISH : PSP_GE_SUBINTR_SIGNAL); SubIntrHandler* handler = get(subintr); if(handler != NULL) { DEBUG_LOG(CPU, "Entering interrupt handler %08x", handler->handlerAddress); currentMIPS->pc = handler->handlerAddress; - u32 data = dl->subIntrToken; + u32 data = intrdata.subIntrToken; currentMIPS->r[MIPS_REG_A0] = data & 0xFFFF; currentMIPS->r[MIPS_REG_A1] = handler->handlerArg; currentMIPS->r[MIPS_REG_A2] = sceKernelGetCompiledSdkVersion() <= 0x02000010 ? 0 : intrdata.pc + 4; @@ -109,11 +112,13 @@ void __GeShutdown() } -void __GeTriggerInterrupt(int listid, u32 pc) +void __GeTriggerInterrupt(int listid, u32 pc, u32 subIntrBase, u16 subIntrToken) { GeInterruptData intrdata; intrdata.listid = listid; intrdata.pc = pc; + intrdata.subIntrBase = subIntrBase; + intrdata.subIntrToken = subIntrToken; ge_pending_cb.push_back(intrdata); __TriggerInterrupt(PSP_INTR_HLE, PSP_GE_INTR, PSP_INTR_SUB_NONE); } diff --git a/Core/HLE/sceGe.h b/Core/HLE/sceGe.h index ce6661fe28..c2621a492f 100644 --- a/Core/HLE/sceGe.h +++ b/Core/HLE/sceGe.h @@ -39,7 +39,7 @@ void Register_sceGe_user(); void __GeInit(); void __GeDoState(PointerWrap &p); void __GeShutdown(); -void __GeTriggerInterrupt(int listid, u32 pc); +void __GeTriggerInterrupt(int listid, u32 pc, u32 subIntrBase, u16 subIntrToken); bool __GeHasPendingInterrupt(); diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index ff1b55d555..3d38b7d7ed 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -451,7 +451,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { currentList->subIntrToken = data & 0xFFFF; // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __GeTriggerInterrupt(currentList->id, currentList->pc); + __GeTriggerInterrupt(currentList->id, currentList->pc, currentList->subIntrBase, currentList->subIntrToken); break; case GE_CMD_END: @@ -489,7 +489,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { } // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __GeTriggerInterrupt(currentList->id, currentList->pc); + __GeTriggerInterrupt(currentList->id, currentList->pc, currentList->subIntrBase, currentList->subIntrToken); } break; case GE_CMD_FINISH: diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index 73517092ac..f9153be506 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -154,7 +154,7 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __GeTriggerInterrupt(currentList->id, currentList->pc); + __GeTriggerInterrupt(currentList->id, currentList->pc, currentList->subIntrBase, currentList->subIntrToken); } break; @@ -188,7 +188,7 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) currentList->subIntrToken = data & 0xFFFF; // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __GeTriggerInterrupt(currentList->id, currentList->pc); + __GeTriggerInterrupt(currentList->id, currentList->pc, currentList->subIntrBase, currentList->subIntrToken); break; case GE_CMD_END: From 87c9aa99c2802eb44a0915e5499bf232c075eddf Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 10 Feb 2013 12:13:35 +0100 Subject: [PATCH 42/43] Let's specify MAX_LOD whether it works or not on gles 2... --- GPU/GLES/Framebuffer.cpp | 8 ++++---- GPU/GLES/TextureCache.cpp | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index f129aa26b3..7090c2a784 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -221,8 +221,8 @@ FramebufferManager::VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { void GetViewportDimensions(int *w, int *h) { float vpXa = getFloat24(gstate.viewportx1); float vpYa = getFloat24(gstate.viewporty1); - *w = fabsf(vpXa * 2); - *h = fabsf(vpYa * 2); + *w = (int)fabsf(vpXa * 2); + *h = (int)fabsf(vpYa * 2); } void FramebufferManager::SetRenderFrameBuffer() { @@ -284,8 +284,8 @@ void FramebufferManager::SetRenderFrameBuffer() { vfb->z_stride = z_stride; vfb->width = drawing_width; vfb->height = drawing_height; - vfb->renderWidth = drawing_width * renderWidthFactor; - vfb->renderHeight = drawing_height * renderHeightFactor; + vfb->renderWidth = (u16)(drawing_width * renderWidthFactor); + vfb->renderHeight = (u16)(drawing_height * renderHeightFactor); vfb->format = fmt; vfb->colorDepth = FBO_8888; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 57893e1551..023e30c181 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -390,11 +390,16 @@ static const GLuint MagFiltGL[2] = { GL_LINEAR }; -// OpenGL ES 2.0 workaround. Let's see if this hackery works. +// OpenGL ES 2.0 workaround. This SHOULD be available but is NOT in the headers in Android. +// Let's see if this hackery works. #ifndef GL_TEXTURE_LOD_BIAS #define GL_TEXTURE_LOD_BIAS 0x8501 #endif +#ifndef GL_TEXTURE_MAX_LOD +#define GL_TEXTURE_MAX_LOD 0x813B +#endif + // This should not have to be done per texture! OpenGL is silly yo // TODO: Dirty-check this against the current texture. void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { @@ -813,9 +818,9 @@ void TextureCache::SetTexture() { LoadTextureLevel(entry, i); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, entry.maxLevel); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, entry.maxLevel); #endif - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)entry.maxLevel); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4.0); UpdateSamplingParams(entry, true); From 78923f55382dd1dbd5dc6673c071f06442c163ab Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 10 Feb 2013 12:14:55 +0100 Subject: [PATCH 43/43] Jit a little more (vfpu single load/store, transfer instructions) --- Core/MIPS/ARM/ArmCompALU.cpp | 6 ++ Core/MIPS/ARM/ArmCompFPU.cpp | 15 ++-- Core/MIPS/ARM/ArmCompLoadStore.cpp | 2 +- Core/MIPS/ARM/ArmCompVFPU.cpp | 10 +++ Core/MIPS/ARM/ArmJit.cpp | 1 + Core/MIPS/ARM/ArmJit.h | 8 ++- Core/MIPS/MIPSTables.cpp | 50 ++++++------- Core/MIPS/x86/CompALU.cpp | 7 ++ Core/MIPS/x86/CompFPU.cpp | 7 +- Core/MIPS/x86/CompVFPU.cpp | 110 ++++++++++++++++++++++++++++- Core/MIPS/x86/Jit.cpp | 2 + Core/MIPS/x86/Jit.h | 8 ++- Core/MIPS/x86/RegCacheFPU.cpp | 4 ++ Core/MIPS/x86/RegCacheFPU.h | 4 ++ 14 files changed, 198 insertions(+), 36 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 2673fcada6..9fdc796c2d 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -277,6 +277,12 @@ namespace MIPSComp } } + void Jit::Comp_Special3(u32 op) + { + // ext, ins + DISABLE; + } + void Jit::Comp_Allegrex(u32 op) { DISABLE diff --git a/Core/MIPS/ARM/ArmCompFPU.cpp b/Core/MIPS/ARM/ArmCompFPU.cpp index 9b3dee1bc4..78a1aa39f0 100644 --- a/Core/MIPS/ARM/ArmCompFPU.cpp +++ b/Core/MIPS/ARM/ArmCompFPU.cpp @@ -28,7 +28,8 @@ #define _POS ((op>>6 ) & 0x1F) #define _SIZE ((op>>11 ) & 0x1F) -#define OLDD Comp_Generic(op); return; +#define CONDITIONAL_DISABLE ; +#define DISABLE Comp_Generic(op); return; namespace MIPSComp { @@ -61,7 +62,7 @@ void Jit::CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), boo void Jit::Comp_FPU3op(u32 op) { - OLDD + DISABLE switch (op & 0x3f) { //case 0: CompFPTriArith(op, &XEmitter::ADDSS, false); break; //F(fd) = F(fs) + F(ft); //add @@ -76,7 +77,7 @@ void Jit::Comp_FPU3op(u32 op) void Jit::Comp_FPULS(u32 op) { - OLDD + DISABLE s32 offset = (s16)(op&0xFFFF); int ft = ((op>>16)&0x1f); @@ -113,9 +114,13 @@ void Jit::Comp_FPULS(u32 op) } } +void Jit::Comp_FPUComp(u32 op) { + DISABLE; +} + void Jit::Comp_FPU2op(u32 op) { - OLDD + DISABLE int fs = _FS; int fd = _FD; @@ -174,7 +179,7 @@ void Jit::Comp_FPU2op(u32 op) void Jit::Comp_mxc1(u32 op) { - OLDD + DISABLE int fs = _FS; int rt = _RT; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 0a3be15da3..fff481a295 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -52,7 +52,7 @@ #define _POS ((op>>6 ) & 0x1F) #define _SIZE ((op>>11 ) & 0x1F) -#define OLDD Comp_Generic(op); return; +#define DISABLE Comp_Generic(op); return; namespace MIPSComp { diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index edb679a7c7..218da28757 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -29,4 +29,14 @@ namespace MIPSComp { DISABLE; } + + void Jit::Comp_Mftv(u32 op) + { + DISABLE; + } + + void Jit::Comp_SV(u32 op) { + DISABLE; + } + } diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index d34cb95f43..7933a76f5a 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -290,6 +290,7 @@ void Jit::LogBlockNumber() INFO_LOG(CPU, "Block number: %i", blocks.GetNumBlocks() - 1); } +void Jit::Comp_DoNothing(u32 op) { } #define _RS ((op>>21) & 0x1F) #define _RT ((op>>16) & 0x1F) diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 1172050eaa..a5bd368f7d 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -86,6 +86,7 @@ public: void Comp_RelBranchRI(u32 op); void Comp_FPUBranch(u32 op); void Comp_FPULS(u32 op); + void Comp_FPUComp(u32 op); void Comp_Jump(u32 op); void Comp_JumpReg(u32 op); void Comp_Syscall(u32 op); @@ -96,13 +97,18 @@ public: void Comp_ShiftType(u32 op); void Comp_Allegrex(u32 op); void Comp_VBranch(u32 op); - void Comp_VDot(u32 op); void Comp_MulDivType(u32 op); + void Comp_Special3(u32 op); void Comp_FPU3op(u32 op); void Comp_FPU2op(u32 op); void Comp_mxc1(u32 op); + void Comp_Mftv(u32 op); + void Comp_VDot(u32 op); + void Comp_DoNothing(u32 op); + + void Comp_SV(u32 op); void Comp_SVQ(u32 op); ArmJitBlockCache *GetBlockCache() { return &blocks; } diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 580ce90510..145e236f46 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -147,7 +147,7 @@ const MIPSInstruction tableImmediate[64] = //xxxxxx ..... //48 INSTR("ll", &Jit::Comp_Generic, Dis_Generic, Int_StoreSync, 0), INSTR("lwc1", &Jit::Comp_FPULS, Dis_FPULS, Int_FPULS, IN_RT|IN_RS_ADDR), - INSTR("lv.s", &Jit::Comp_Generic, Dis_SV, Int_SV, IS_VFPU), + INSTR("lv.s", &Jit::Comp_SV, Dis_SV, Int_SV, IS_VFPU), {-2}, // HIT THIS IN WIPEOUT {VFPU4Jump}, INSTR("lv", &Jit::Comp_SVQ, Dis_SVLRQ, Int_SVQ, IS_VFPU), @@ -156,7 +156,7 @@ const MIPSInstruction tableImmediate[64] = //xxxxxx ..... //56 INSTR("sc", &Jit::Comp_Generic, Dis_Generic, Int_StoreSync, 0), INSTR("swc1", &Jit::Comp_FPULS, Dis_FPULS, Int_FPULS, 0), //copU - INSTR("sv.s", &Jit::Comp_Generic, Dis_SV, Int_SV,IS_VFPU), + INSTR("sv.s", &Jit::Comp_SV, Dis_SV, Int_SV,IS_VFPU), {-2}, //60 {VFPU6}, @@ -185,7 +185,7 @@ const MIPSInstruction tableSpecial[64] = /// 000000 ...... ...... .......... xxx INSTR("syscall", &Jit::Comp_Syscall, Dis_Syscall, Int_Syscall,0), INSTR("break", &Jit::Comp_Break, Dis_Generic, Int_Break, 0), {-2}, - INSTR("sync", &Jit::Comp_Generic, Dis_Generic, Int_Sync, 0), + INSTR("sync", &Jit::Comp_DoNothing, Dis_Generic, Int_Sync, 0), //16 INSTR("mfhi", &Jit::Comp_MulDivType, Dis_FromHiloTransfer, Int_MulDivType, OUT_RD|IN_OTHER), @@ -224,8 +224,8 @@ const MIPSInstruction tableSpecial[64] = /// 000000 ...... ...... .......... xxx INSTR("sltu", &Jit::Comp_RType3, Dis_RType3, Int_RType3,IN_RS|IN_RT|OUT_RD), INSTR("max", &Jit::Comp_RType3, Dis_RType3, Int_RType3,IN_RS|IN_RT|OUT_RD), INSTR("min", &Jit::Comp_RType3, Dis_RType3, Int_RType3,IN_RS|IN_RT|OUT_RD), - INSTR("msub", &Jit::Comp_Generic, Dis_MulDivType, Int_MulDivType, IN_RS|IN_RT|OUT_OTHER), - INSTR("msubu", &Jit::Comp_Generic, Dis_MulDivType, Int_MulDivType, IN_RS|IN_RT|OUT_OTHER), + INSTR("msub", &Jit::Comp_RType3, Dis_MulDivType, Int_MulDivType, IN_RS|IN_RT|OUT_OTHER), + INSTR("msubu", &Jit::Comp_RType3, Dis_MulDivType, Int_MulDivType, IN_RS|IN_RT|OUT_OTHER), //48 INSTR("tge", &Jit::Comp_Generic, Dis_RType3, 0, 0), @@ -276,32 +276,32 @@ const MIPSInstruction tableSpecial2[64] = //40 {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, //48 - INSTR("c.f", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.un", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.eq", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ueq", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.olt", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ult", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ole", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ule", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.sf", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ngle",&Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.seq", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ngl", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.lt", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.nge", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.le", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), - INSTR("c.ngt", &Jit::Comp_Generic, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.f", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.un", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.eq", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ueq", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.olt", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ult", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ole", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ule", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.sf", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ngle",&Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.seq", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ngl", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.lt", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.nge", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.le", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), + INSTR("c.ngt", &Jit::Comp_FPUComp, Dis_FPUComp, Int_FPUComp, OUT_FPUFLAG), }; const MIPSInstruction tableSpecial3[64] = { - INSTR("ext", &Jit::Comp_Generic, Dis_Special3, Int_Special3, IN_RS|OUT_RT), + INSTR("ext", &Jit::Comp_Special3, Dis_Special3, Int_Special3, IN_RS|OUT_RT), {-2}, {-2}, {-2}, - INSTR("ins", &Jit::Comp_Generic, Dis_Special3, Int_Special3, IN_RS|OUT_RT), + INSTR("ins", &Jit::Comp_Special3, Dis_Special3, Int_Special3, IN_RS|OUT_RT), {-2}, {-2}, {-2}, @@ -363,11 +363,11 @@ const MIPSInstruction tableCop2[32] = INSTR("mfc2", &Jit::Comp_Generic, Dis_Generic, 0, OUT_RT), {-2}, INSTR("cfc2", &Jit::Comp_Generic, Dis_Generic, 0, 0), - INSTR("mfv", &Jit::Comp_Generic, Dis_Mftv, Int_Mftv, 0), + INSTR("mfv", &Jit::Comp_Mftv, Dis_Mftv, Int_Mftv, 0), INSTR("mtc2", &Jit::Comp_Generic, Dis_Generic, 0, IN_RT), {-2}, INSTR("ctc2", &Jit::Comp_Generic, Dis_Generic, 0, 0), - INSTR("mtv", &Jit::Comp_Generic, Dis_Mftv, Int_Mftv, 0), + INSTR("mtv", &Jit::Comp_Mftv, Dis_Mftv, Int_Mftv, 0), {Cop2BC2}, INSTR("??", &Jit::Comp_Generic, Dis_Generic, 0, 0), diff --git a/Core/MIPS/x86/CompALU.cpp b/Core/MIPS/x86/CompALU.cpp index 1f2cf0943f..2ffcd94d4b 100644 --- a/Core/MIPS/x86/CompALU.cpp +++ b/Core/MIPS/x86/CompALU.cpp @@ -335,6 +335,13 @@ namespace MIPSComp } } + void Jit::Comp_Special3(u32 op) + { + // ext, ins + DISABLE; + } + + void Jit::Comp_Allegrex(u32 op) { CONDITIONAL_DISABLE diff --git a/Core/MIPS/x86/CompFPU.cpp b/Core/MIPS/x86/CompFPU.cpp index 7494d649f9..54f7459ff6 100644 --- a/Core/MIPS/x86/CompFPU.cpp +++ b/Core/MIPS/x86/CompFPU.cpp @@ -147,8 +147,11 @@ void Jit::Comp_FPULS(u32 op) static const u64 GC_ALIGNED16(ssSignBits2[2]) = {0x8000000080000000ULL, 0x8000000080000000ULL}; static const u64 GC_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL}; -void Jit::Comp_FPU2op(u32 op) -{ +void Jit::Comp_FPUComp(u32 op) { + DISABLE; +} + +void Jit::Comp_FPU2op(u32 op) { CONDITIONAL_DISABLE; int fs = _FS; diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 32a859d19b..bee71be757 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -141,11 +141,77 @@ void Jit::ApplyPrefixD(const u8 *vregs, u32 prefix, VectorSize sz, bool onlyWrit static u32 GC_ALIGNED16(ssLoadStoreTemp[1]); +void Jit::Comp_SV(u32 op) { + // DISABLE; + + s32 imm = (signed short)(op&0xFFFC); + int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); + int rs = _RS; + + switch (op >> 26) + { + case 50: //lv.s // VI(vt) = Memory::Read_U32(addr); + { + gpr.BindToRegister(rs, true, false); + fpr.MapRegV(vt, MAP_NOINIT); + + JitSafeMem safe(this, rs, imm); + safe.SetFar(); + OpArg src; + if (safe.PrepareRead(src)) + { + MOVSS(fpr.VX(vt), safe.NextFastAddress(0)); + } + if (safe.PrepareSlowRead((void *) &Memory::Read_U32)) + { + safe.NextSlowRead((void *) &Memory::Read_U32, 0); + MOV(32, M((void *)&ssLoadStoreTemp), R(EAX)); + MOVSS(fpr.VX(vt), M((void *)&ssLoadStoreTemp)); + } + safe.Finish(); + + gpr.UnlockAll(); + fpr.ReleaseSpillLocks(); + } + break; + + case 58: //sv.s // Memory::Write_U32(VI(vt), addr); + { + gpr.BindToRegister(rs, true, true); + + // Even if we don't use real SIMD there's still 8 or 16 scalar float registers. + fpr.MapRegV(vt, 0); + + JitSafeMem safe(this, rs, imm); + safe.SetFar(); + OpArg dest; + if (safe.PrepareWrite(dest)) + { + MOVSS(safe.NextFastAddress(0), fpr.VX(vt)); + } + if (safe.PrepareSlowWrite()) + { + MOVSS(M((void *)&ssLoadStoreTemp), fpr.VX(vt)); + safe.DoSlowWrite((void *) &Memory::Write_U32, M((void *)&ssLoadStoreTemp), 0); + } + safe.Finish(); + + fpr.ReleaseSpillLocks(); + gpr.UnlockAll(); + } + break; + + default: + _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + break; + } +} + void Jit::Comp_SVQ(u32 op) { int imm = (signed short)(op&0xFFFC); - int rs = _RS; int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); + int rs = _RS; switch (op >> 26) { @@ -263,5 +329,47 @@ void Jit::Comp_VDot(u32 op) { js.EatPrefix(); } +void Jit::Comp_Mftv(u32 op) { + int imm = op & 0xFF; + int rt = _RT; + switch ((op >> 21) & 0x1f) + { + case 3: //mfv / mfvc + if (imm < 128) { //R(rt) = VI(imm); + fpr.StoreFromRegisterV(imm); + gpr.BindToRegister(rt, false, true); + MOV(32, gpr.R(rt), fpr.V(imm)); + } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc + gpr.BindToRegister(rt, false, true); + MOV(32, gpr.R(rt), M(¤tMIPS->vfpuCtrl[imm - 128])); + } else if (rt == 0 && imm == 255) { + // This appears to be used as a CPU interlock by some games. Do nothing. + } else { + //ERROR - maybe need to make this value too an "interlock" value? + _dbg_assert_msg_(CPU,0,"mfv - invalid register"); + } + break; + + case 7: //mtv + if (imm < 128) { + fpr.StoreFromRegisterV(imm); + gpr.BindToRegister(rt, true, false); + MOV(32, fpr.V(imm), gpr.R(rt)); + // VI(imm) = R(rt); + } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc //currentMIPS->vfpuCtrl[imm - 128] = R(rt); + gpr.BindToRegister(rt, true, false); + MOV(32, M(¤tMIPS->vfpuCtrl[imm - 128]), gpr.R(rt)); + } else { + //ERROR + _dbg_assert_msg_(CPU,0,"mtv - invalid register"); + } + break; + + default: + DISABLE; + _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + break; + } +} } \ No newline at end of file diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 6f26a4bc22..dc6a789830 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -569,4 +569,6 @@ void Jit::JitSafeMem::Finish() jit_->SetJumpTarget(skip_); } +void Jit::Comp_DoNothing(u32 op) { } + } // namespace diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 234c0698ce..ff23c4d348 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -118,6 +118,7 @@ public: void Comp_RelBranchRI(u32 op); void Comp_FPUBranch(u32 op); void Comp_FPULS(u32 op); + void Comp_FPUComp(u32 op); void Comp_Jump(u32 op); void Comp_JumpReg(u32 op); void Comp_Syscall(u32 op); @@ -129,15 +130,20 @@ public: void Comp_Allegrex(u32 op); void Comp_VBranch(u32 op); void Comp_MulDivType(u32 op); + void Comp_Special3(u32 op); void Comp_FPU3op(u32 op); void Comp_FPU2op(u32 op); void Comp_mxc1(u32 op); + void Comp_SV(u32 op); void Comp_SVQ(u32 op); void Comp_VPFX(u32 op); void Comp_VDot(u32 op); - + void Comp_Mftv(u32 op); + + void Comp_DoNothing(u32 op); + void ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz); void ApplyPrefixD(const u8 *vregs, u32 prefix, VectorSize sz, bool onlyWriteMask = false); diff --git a/Core/MIPS/x86/RegCacheFPU.cpp b/Core/MIPS/x86/RegCacheFPU.cpp index 9a2027e1e3..9c2cb9c837 100644 --- a/Core/MIPS/x86/RegCacheFPU.cpp +++ b/Core/MIPS/x86/RegCacheFPU.cpp @@ -59,6 +59,10 @@ void FPURegCache::SpillLockV(int vec, VectorSize sz) { SpillLockV(v, sz); } +void FPURegCache::MapRegV(int vreg, int flags) { + BindToRegister(vreg + 32, (flags & MAP_NOINIT) == 0, (flags & MAP_DIRTY) != 0); +} + void FPURegCache::MapRegsV(int vec, VectorSize sz, int flags) { u8 v[4]; GetVectorRegs(v, sz, vec); diff --git a/Core/MIPS/x86/RegCacheFPU.h b/Core/MIPS/x86/RegCacheFPU.h index 4102ef5ee6..cb4db210df 100644 --- a/Core/MIPS/x86/RegCacheFPU.h +++ b/Core/MIPS/x86/RegCacheFPU.h @@ -64,6 +64,9 @@ public: void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats); void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true); void StoreFromRegister(int preg); + void StoreFromRegisterV(int preg) { + StoreFromRegister(preg + 32); + } OpArg GetDefaultLocation(int reg) const; void SetEmitter(XEmitter *emitter) {emit = emitter;} @@ -94,6 +97,7 @@ public: void SpillLock(int p1, int p2=0xff, int p3=0xff, int p4=0xff); void ReleaseSpillLocks(); + void MapRegV(int vreg, int flags); void MapRegsV(int vec, VectorSize vsz, int flags); void MapRegsV(const u8 *v, VectorSize vsz, int flags); void SpillLockV(const u8 *v, VectorSize vsz);