Vita3K/vita3k/modules/SceDisplay/SceDisplay.cpp

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)