/*
This file is part of jpcsp.
Jpcsp 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 3 of the License, or
(at your option) any later version.
Jpcsp 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 Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.util;
import static jpcsp.graphics.GeCommands.PRIM_LINE;
import static jpcsp.graphics.GeCommands.TFUNC_FRAGMENT_DOUBLE_ENABLE_COLOR_DOUBLED;
import static jpcsp.graphics.GeCommands.TFUNC_FRAGMENT_DOUBLE_ENABLE_COLOR_UNTOUCHED;
import static jpcsp.graphics.GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_COLOR_ALPHA_IS_IGNORED;
import static jpcsp.graphics.GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_COLOR_ALPHA_IS_READ;
import static jpcsp.graphics.GeCommands.VTYPE_COLOR_FORMAT_32BIT_ABGR_8888;
import static jpcsp.graphics.GeCommands.VTYPE_POSITION_FORMAT_16_BIT;
import static jpcsp.graphics.GeCommands.VTYPE_TRANSFORM_PIPELINE_RAW_COORD;
import org.apache.log4j.Logger;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.HLE.Modules;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.modules.SysMemUserForUser;
import jpcsp.HLE.modules.sceGe_user;
import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo;
import jpcsp.graphics.GeCommands;
import jpcsp.graphics.GeContext;
import jpcsp.graphics.RE.IRenderingEngine;
import jpcsp.hardware.Screen;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryWriter;
/**
* Utility class to draw to the PSP GE from inside HLE functions.
* Used for example by sceUtilitySavedata to render its user interface.
*
* @author gid15
*
*/
public class sceGu {
private static Logger log = Logger.getLogger("sceGu");
private SysMemInfo sysMemInfo;
private int bottomAddr;
private int topAddr;
private int listAddr;
private IMemoryWriter listWriter;
private int listId = -1;
public sceGu(int totalMemorySize) {
sysMemInfo = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "sceGu", SysMemUserForUser.PSP_SMEM_Low, totalMemorySize, 0);
if (sysMemInfo == null) {
log.error(String.format("Cannot allocate sceGu memory, size=0x%X", totalMemorySize));
}
}
public int sceGuGetMemory(int size) {
size = Utilities.alignUp(size, 3);
if (topAddr - size < listWriter.getCurrentAddress()) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceGuGetMemory size=0x%X - not enough memory, available=0x%X", size, topAddr - listWriter.getCurrentAddress()));
}
// Not enough memory available
return 0;
}
topAddr -= size;
return topAddr;
}
public void free() {
if (sysMemInfo != null) {
Modules.SysMemUserForUserModule.free(sysMemInfo);
sysMemInfo = null;
}
bottomAddr = 0;
topAddr = 0;
listWriter = null;
}
public void sendCommandi(int cmd, int argument) {
listWriter.writeNext((cmd << 24) | (argument & 0x00FFFFFF));
}
public void sendCommandf(int cmd, float argument) {
sendCommandi(cmd, Float.floatToRawIntBits(argument) >> 8);
}
protected void sendCommandBase(int cmd, int address) {
sendCommandi(GeCommands.BASE, (address & 0xFF000000) >>> 8);
sendCommandi(cmd, address & 0x00FFFFFF);
}
public void sceGuStart() {
if (sysMemInfo != null) {
bottomAddr = sysMemInfo.addr;
topAddr = sysMemInfo.addr + sysMemInfo.size;
} else {
// Reserve memory for 2 complete screens (double buffering)
int reservedSize = 512 * Screen.height * 4 * 2;
// Use the rest of the VRAM
bottomAddr = MemoryMap.START_VRAM + reservedSize;
topAddr = bottomAddr + (MemoryMap.SIZE_VRAM - reservedSize);
}
listAddr = bottomAddr;
listWriter = MemoryWriter.getMemoryWriter(listAddr, 4);
listId = -1;
// Init some values
sceGuOffsetAddr(0);
sendCommandi(GeCommands.BASE, 0);
}
public void sceGuFinish() {
sendCommandi(GeCommands.FINISH, 0);
sendCommandi(GeCommands.END, 0);
if (topAddr < listWriter.getCurrentAddress()) {
log.error(String.format("sceGu memory overwrite mem=%s, listAddr=0x%08X, topAddr=0x%08X", sysMemInfo, listWriter.getCurrentAddress(), topAddr));
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("sceGu memory usage free=0x%X, mem=%s, listAddr=0x%08X, topAddr=0x%08X", topAddr - listWriter.getCurrentAddress(), sysMemInfo, listWriter.getCurrentAddress(), topAddr));
}
}
listWriter.flush();
int saveContextAddr = sceGuGetMemory(GeContext.SIZE_OF);
Memory mem = Memory.getInstance();
listId = Modules.sceGe_userModule.hleGeListEnQueue(new TPointer(mem, listAddr), TPointer.NULL, -1, TPointer.NULL, saveContextAddr, false);
}
public boolean isListDrawing() {
if (listId < 0) {
return false;
}
int listState = Modules.sceGe_userModule.hleGeListSync(listId);
if (log.isDebugEnabled()) {
log.debug(String.format("sceGu list 0x%X: state %d", listId, listState));
}
return listState == sceGe_user.PSP_GE_LIST_DRAWING || listState == sceGe_user.PSP_GE_LIST_QUEUED;
}
private void sceGuSetFlag(int flag, int value) {
switch (flag) {
case IRenderingEngine.GU_ALPHA_TEST: sendCommandi(GeCommands.ATE, value); break;
case IRenderingEngine.GU_DEPTH_TEST: sendCommandi(GeCommands.ZTE, value); break;
case IRenderingEngine.GU_SCISSOR_TEST: break;
case IRenderingEngine.GU_STENCIL_TEST: sendCommandi(GeCommands.STE, value); break;
case IRenderingEngine.GU_BLEND: sendCommandi(GeCommands.ABE, value); break;
case IRenderingEngine.GU_CULL_FACE: sendCommandi(GeCommands.BCE, value); break;
case IRenderingEngine.GU_DITHER: sendCommandi(GeCommands.DTE, value); break;
case IRenderingEngine.GU_FOG: sendCommandi(GeCommands.FGE, value); break;
case IRenderingEngine.GU_CLIP_PLANES: sendCommandi(GeCommands.CPE, value); break;
case IRenderingEngine.GU_TEXTURE_2D: sendCommandi(GeCommands.TME, value); break;
case IRenderingEngine.GU_LIGHTING: sendCommandi(GeCommands.LTE, value); break;
case IRenderingEngine.GU_LIGHT0: sendCommandi(GeCommands.LTE0, value); break;
case IRenderingEngine.GU_LIGHT1: sendCommandi(GeCommands.LTE1, value); break;
case IRenderingEngine.GU_LIGHT2: sendCommandi(GeCommands.LTE2, value); break;
case IRenderingEngine.GU_LIGHT3: sendCommandi(GeCommands.LTE3, value); break;
case IRenderingEngine.GU_LINE_SMOOTH: sendCommandi(GeCommands.AAE, value); break;
case IRenderingEngine.GU_PATCH_CULL_FACE: sendCommandi(GeCommands.PCE, value); break;
case IRenderingEngine.GU_COLOR_TEST: sendCommandi(GeCommands.CTE, value); break;
case IRenderingEngine.GU_COLOR_LOGIC_OP: sendCommandi(GeCommands.LOE, value); break;
case IRenderingEngine.GU_FACE_NORMAL_REVERSE: sendCommandi(GeCommands.RNORM, value); break;
case IRenderingEngine.GU_PATCH_FACE: sendCommandi(GeCommands.PFACE, value); break;
case IRenderingEngine.GU_FRAGMENT_2X: break;
}
}
public void sceGuEnable(int flag) {
sceGuSetFlag(flag, 1);
}
public void sceGuDisable(int flag) {
sceGuSetFlag(flag, 0);
}
public void sceGuDrawArray(int prim, int vtype, int count, int indices, int vertices) {
if (vtype != 0) {
sendCommandi(GeCommands.VTYPE, vtype);
}
if (indices != 0) {
sendCommandBase(GeCommands.IADDR, indices);
}
if (vertices != 0) {
sendCommandBase(GeCommands.VADDR, vertices);
}
sendCommandi(GeCommands.PRIM, (prim << 16) | (count & 0xFFFF));
}
private int getExp(int val) {
int i;
for (i = 9; i > 0 && ((val >> i) & 1) == 0; i--) {
}
return i;
}
public void sceGuTexImage(int mipmap, int width, int height, int tbw, int tbp) {
sendCommandi(GeCommands.TBP0 + mipmap, tbp & 0x00FFFFFF);
sendCommandi(GeCommands.TBW0 + mipmap, ((tbp & 0xFF000000) >>> 8) | (tbw & 0xFFFF));
sendCommandi(GeCommands.TSIZE0 + mipmap, (getExp(height) << 8) | getExp(width));
sendCommandi(GeCommands.TFLUSH, 0);
}
public void sceGuTexMode(int tpsm, int maxMipmaps, boolean swizzle) {
sendCommandi(GeCommands.TMODE, (maxMipmaps << 16) | (swizzle ? 1 : 0));
sendCommandi(GeCommands.TPSM, tpsm);
}
public void sceGuClutMode(int cpsm, int shift, int mask, int start) {
sendCommandi(GeCommands.CMODE, cpsm | (shift << 12) | (mask << 8) | (start << 16));
}
public void sceGuClutLoad(int numBlocks, int cbp) {
sendCommandi(GeCommands.CBP, cbp & 0x00FFFFFF);
sendCommandi(GeCommands.CBPH, (cbp & 0xFF000000) >>> 8);
sendCommandi(GeCommands.CLOAD, numBlocks);
}
public void sceGuTexFunc(int textureFunc, boolean textureAlphaUsed, boolean textureColorDoubled) {
sendCommandi(GeCommands.TFUNC, textureFunc | ((textureAlphaUsed ? TFUNC_FRAGMENT_DOUBLE_TEXTURE_COLOR_ALPHA_IS_READ : TFUNC_FRAGMENT_DOUBLE_TEXTURE_COLOR_ALPHA_IS_IGNORED) << 8) | ((textureColorDoubled ? TFUNC_FRAGMENT_DOUBLE_ENABLE_COLOR_DOUBLED : TFUNC_FRAGMENT_DOUBLE_ENABLE_COLOR_UNTOUCHED) << 16));
}
public void sceGuBlendFunc(int op, int src, int dest, int srcFix, int destFix) {
sendCommandi(GeCommands.ALPHA, src | (dest << 4) | (op << 8));
if (src >= GeCommands.ALPHA_FIX) {
sendCommandi(GeCommands.SFIX, srcFix);
}
if (dest >= GeCommands.ALPHA_FIX) {
sendCommandi(GeCommands.DFIX, destFix);
}
}
public void sceGuOffsetAddr(int offsetAddr) {
sendCommandi(GeCommands.OFFSET_ADDR, offsetAddr >>> 8);
}
public void sceGuTexEnvColor(int color) {
sendCommandi(GeCommands.TEC, color & 0x00FFFFFF);
}
public void sceGuTexWrap(int u, int v) {
sendCommandi(GeCommands.TWRAP, (v << 8) | u);
}
public void sceGuTexFilter(int min, int mag) {
sendCommandi(GeCommands.TFLT, (mag << 8) | min);
}
public void sceGuDrawHorizontalLine(int x0, int x1, int y, int color) {
sceGuDrawLine(x0, y, x1, y, color);
}
public void sceGuDrawLine(int x0, int y0, int x1, int y1, int color) {
int numberOfVertex = 2;
int lineVertexAddr = sceGuGetMemory(12 * numberOfVertex);
IMemoryWriter lineVertexWriter = MemoryWriter.getMemoryWriter(lineVertexAddr, 2);
// Color
lineVertexWriter.writeNext(color & 0xFFFF);
lineVertexWriter.writeNext(color >>> 16);
// Position
lineVertexWriter.writeNext(x0);
lineVertexWriter.writeNext(y0);
lineVertexWriter.writeNext(0);
// Align on 32-bit
lineVertexWriter.writeNext(0);
// Color
lineVertexWriter.writeNext(color & 0xFFFF);
lineVertexWriter.writeNext(color >>> 16);
// Position
lineVertexWriter.writeNext(x1);
lineVertexWriter.writeNext(y1);
lineVertexWriter.writeNext(0);
// Align on 32-bit
lineVertexWriter.writeNext(0);
lineVertexWriter.flush();
sceGuDisable(IRenderingEngine.GU_TEXTURE_2D);
sceGuDrawArray(PRIM_LINE, (VTYPE_TRANSFORM_PIPELINE_RAW_COORD << 23) | (VTYPE_COLOR_FORMAT_32BIT_ABGR_8888 << 2) | (VTYPE_POSITION_FORMAT_16_BIT << 7), numberOfVertex, 0, lineVertexAddr);
}
public void sceGuDrawRectangle(int x0, int y0, int x1, int y1, int color) {
int numberOfVertex = 2;
int lineVertexAddr = sceGuGetMemory(12 * numberOfVertex);
IMemoryWriter lineVertexWriter = MemoryWriter.getMemoryWriter(lineVertexAddr, 2);
// Color
lineVertexWriter.writeNext(color & 0xFFFF);
lineVertexWriter.writeNext(color >>> 16);
// Position
lineVertexWriter.writeNext(x0);
lineVertexWriter.writeNext(y0);
lineVertexWriter.writeNext(0);
// Align on 32-bit
lineVertexWriter.writeNext(0);
// Color
lineVertexWriter.writeNext(color & 0xFFFF);
lineVertexWriter.writeNext(color >>> 16);
// Position
lineVertexWriter.writeNext(x1);
lineVertexWriter.writeNext(y1);
lineVertexWriter.writeNext(0);
// Align on 32-bit
lineVertexWriter.writeNext(0);
lineVertexWriter.flush();
sceGuDisable(IRenderingEngine.GU_TEXTURE_2D);
sceGuDrawArray(GeCommands.PRIM_SPRITES, (VTYPE_TRANSFORM_PIPELINE_RAW_COORD << 23) | (VTYPE_COLOR_FORMAT_32BIT_ABGR_8888 << 2) | (VTYPE_POSITION_FORMAT_16_BIT << 7), numberOfVertex, 0, lineVertexAddr);
}
public void sceGuClear(int mode, int color) {
sendCommandi(GeCommands.CLEAR, ((mode & 0x7) << 8) | 0x01);
sceGuDrawRectangle(0, 0, Screen.width, Screen.height, color);
sendCommandi(GeCommands.CLEAR, 0x00);
}
}