mirror of
https://github.com/Vita3K/Vita3K.git
synced 2025-04-02 11:02:10 -04:00
240 lines
8.1 KiB
C++
240 lines
8.1 KiB
C++
// Vita3K emulator project
|
|
// Copyright (C) 2022 Vita3K team
|
|
//
|
|
// 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; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// 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 for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
#ifdef TRACY_ENABLE
|
|
#include "Tracy.hpp"
|
|
#endif
|
|
|
|
#include "SceDisplay.h"
|
|
|
|
#include <display/functions.h>
|
|
#include <host/functions.h>
|
|
#include <util/lock_and_find.h>
|
|
#include <util/types.h>
|
|
|
|
static int display_wait(HostState &host, SceUID thread_id, int vcount, const bool is_since_setbuf, const bool is_cb) {
|
|
const auto &thread = host.kernel.get_thread(thread_id);
|
|
|
|
// this part should not need a mutex
|
|
const uint64_t vblank_count = host.display.vblank_count.load();
|
|
if (is_since_setbuf) {
|
|
vcount = host.display.last_setframe_vblank_count + vcount - vblank_count;
|
|
} else {
|
|
// the wait is considered starting from the last time the thread resumed
|
|
// from a vblank wait (sceDisplayWait...) and not from the time this function was called
|
|
// but we still need to wait at least for one vblank
|
|
const uint64_t next_vsync = vblank_count + 1;
|
|
const uint64_t min_vsync = thread->last_vblank_waited + vcount;
|
|
thread->last_vblank_waited = std::max(next_vsync, min_vsync);
|
|
vcount = static_cast<int>(thread->last_vblank_waited - vblank_count);
|
|
}
|
|
|
|
if (vcount > 0)
|
|
wait_vblank(host.display, host.kernel, thread, vcount, is_cb);
|
|
|
|
if (host.display.abort.load())
|
|
return SCE_DISPLAY_ERROR_NO_PIXEL_DATA;
|
|
|
|
return SCE_DISPLAY_ERROR_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, _sceDisplayGetFrameBuf, SceDisplayFrameBuf *pFrameBuf, SceDisplaySetBufSync sync) {
|
|
if (pFrameBuf->size != sizeof(SceDisplayFrameBuf))
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_VALUE);
|
|
else if (sync != SCE_DISPLAY_SETBUF_NEXTFRAME && sync != SCE_DISPLAY_SETBUF_IMMEDIATE)
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_UPDATETIMING);
|
|
|
|
const std::lock_guard<std::mutex> guard(host.display.display_info_mutex);
|
|
|
|
DisplayFrameInfo *info;
|
|
// ignore value of sync in GetFrameBuf
|
|
if (host.display.has_next_frame) {
|
|
info = &host.display.next_frame;
|
|
} else {
|
|
info = &host.display.frame;
|
|
}
|
|
|
|
pFrameBuf->base = info->base;
|
|
pFrameBuf->pitch = info->pitch;
|
|
pFrameBuf->pixelformat = info->pixelformat;
|
|
pFrameBuf->width = info->image_size.x;
|
|
pFrameBuf->height = info->image_size.y;
|
|
|
|
return SCE_DISPLAY_ERROR_OK;
|
|
}
|
|
|
|
EXPORT(int, _sceDisplayGetFrameBufInternal) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, _sceDisplayGetMaximumFrameBufResolution) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, _sceDisplayGetResolutionInfoInternal) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(SceInt32, _sceDisplaySetFrameBuf, const SceDisplayFrameBuf *pFrameBuf, SceDisplaySetBufSync sync) {
|
|
if (!pFrameBuf)
|
|
return SCE_DISPLAY_ERROR_OK;
|
|
if (pFrameBuf->size != sizeof(SceDisplayFrameBuf)) {
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_VALUE);
|
|
}
|
|
if (!pFrameBuf->base) {
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_ADDR);
|
|
}
|
|
if (pFrameBuf->pitch < pFrameBuf->width) {
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_PITCH);
|
|
}
|
|
if (pFrameBuf->pixelformat != SCE_DISPLAY_PIXELFORMAT_A8B8G8R8) {
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_PIXELFORMAT);
|
|
}
|
|
if (sync != SCE_DISPLAY_SETBUF_NEXTFRAME && sync != SCE_DISPLAY_SETBUF_IMMEDIATE) {
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_UPDATETIMING);
|
|
}
|
|
if ((pFrameBuf->width < 480) || (pFrameBuf->height < 272) || (pFrameBuf->pitch < 480))
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_RESOLUTION);
|
|
|
|
{
|
|
const std::lock_guard<std::mutex> guard(host.display.display_info_mutex);
|
|
|
|
DisplayFrameInfo *info;
|
|
if (sync == SCE_DISPLAY_SETBUF_NEXTFRAME) {
|
|
info = &host.display.next_frame;
|
|
host.display.has_next_frame = true;
|
|
} else {
|
|
// we are supposed to swap the displayed buffer in the middle of the frame
|
|
// which we do not support
|
|
STUBBED("SCE_DISPLAY_SETBUF_IMMEDIATE is not supported");
|
|
info = &host.display.frame;
|
|
}
|
|
|
|
info->base = pFrameBuf->base;
|
|
info->pitch = pFrameBuf->pitch;
|
|
info->pixelformat = pFrameBuf->pixelformat;
|
|
info->image_size.x = pFrameBuf->width;
|
|
info->image_size.y = pFrameBuf->height;
|
|
host.display.last_setframe_vblank_count = host.display.vblank_count.load();
|
|
}
|
|
|
|
host.frame_count++;
|
|
|
|
#ifdef TRACY_ENABLE
|
|
FrameMarkNamed("SCE frame buffer"); // Tracy - Secondary frame end mark for the emulated frame buffer
|
|
#endif
|
|
|
|
return SCE_DISPLAY_ERROR_OK;
|
|
}
|
|
|
|
EXPORT(int, _sceDisplaySetFrameBufForCompat) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, _sceDisplaySetFrameBufInternal) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceDisplayGetPrimaryHead) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayGetRefreshRate, float *pFps) {
|
|
*pFps = 59.94005f;
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayGetVcount) {
|
|
return static_cast<SceInt32>(host.display.vblank_count.load()) & 0xFFFF;
|
|
}
|
|
|
|
EXPORT(int, sceDisplayGetVcountInternal) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayRegisterVblankStartCallback, SceUID uid) {
|
|
const auto cb = lock_and_find(uid, host.kernel.callbacks, host.kernel.mutex);
|
|
if (!cb)
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_VALUE);
|
|
|
|
host.display.vblank_callbacks[uid] = cb;
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayUnregisterVblankStartCallback, SceUID uid) {
|
|
if (host.display.vblank_callbacks.find(uid) == host.display.vblank_callbacks.end())
|
|
return RET_ERROR(SCE_DISPLAY_ERROR_INVALID_VALUE);
|
|
|
|
host.display.vblank_callbacks.erase(uid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitSetFrameBuf) {
|
|
return display_wait(host, thread_id, 1, true, false);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitSetFrameBufCB) {
|
|
return display_wait(host, thread_id, 1, true, true);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitSetFrameBufMulti, SceUInt vcount) {
|
|
return display_wait(host, thread_id, static_cast<int>(vcount), true, false);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitSetFrameBufMultiCB, SceUInt vcount) {
|
|
return display_wait(host, thread_id, static_cast<int>(vcount), true, true);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitVblankStart) {
|
|
return display_wait(host, thread_id, 1, false, false);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitVblankStartCB) {
|
|
return display_wait(host, thread_id, 1, false, true);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitVblankStartMulti, SceUInt vcount) {
|
|
return display_wait(host, thread_id, static_cast<int>(vcount), false, false);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceDisplayWaitVblankStartMultiCB, SceUInt vcount) {
|
|
return display_wait(host, thread_id, static_cast<int>(vcount), false, true);
|
|
}
|
|
|
|
BRIDGE_IMPL(_sceDisplayGetFrameBuf)
|
|
BRIDGE_IMPL(_sceDisplayGetFrameBufInternal)
|
|
BRIDGE_IMPL(_sceDisplayGetMaximumFrameBufResolution)
|
|
BRIDGE_IMPL(_sceDisplayGetResolutionInfoInternal)
|
|
BRIDGE_IMPL(_sceDisplaySetFrameBuf)
|
|
BRIDGE_IMPL(_sceDisplaySetFrameBufForCompat)
|
|
BRIDGE_IMPL(_sceDisplaySetFrameBufInternal)
|
|
BRIDGE_IMPL(sceDisplayGetPrimaryHead)
|
|
BRIDGE_IMPL(sceDisplayGetRefreshRate)
|
|
BRIDGE_IMPL(sceDisplayGetVcount)
|
|
BRIDGE_IMPL(sceDisplayGetVcountInternal)
|
|
BRIDGE_IMPL(sceDisplayRegisterVblankStartCallback)
|
|
BRIDGE_IMPL(sceDisplayUnregisterVblankStartCallback)
|
|
BRIDGE_IMPL(sceDisplayWaitSetFrameBuf)
|
|
BRIDGE_IMPL(sceDisplayWaitSetFrameBufCB)
|
|
BRIDGE_IMPL(sceDisplayWaitSetFrameBufMulti)
|
|
BRIDGE_IMPL(sceDisplayWaitSetFrameBufMultiCB)
|
|
BRIDGE_IMPL(sceDisplayWaitVblankStart)
|
|
BRIDGE_IMPL(sceDisplayWaitVblankStartCB)
|
|
BRIDGE_IMPL(sceDisplayWaitVblankStartMulti)
|
|
BRIDGE_IMPL(sceDisplayWaitVblankStartMultiCB)
|