the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 274 lines 9.6 kB view raw
1/* SCE CONFIDENTIAL 2PlayStation(R)3 Programmer Tool Runtime Library 430.001 3* Copyright (C) 2007 Sony Computer Entertainment Inc. 4* All Rights Reserved. 5*/ 6 7/* common headers */ 8#include <stdint.h> 9#include <stdlib.h> 10#include <alloca.h> 11#include <spu_intrinsics.h> 12#include <cell/spurs.h> 13#include <cell/dma.h> 14#include <cell/spurs/job_queue.h> 15 16#include "LevelRenderer_FindNearestChunk.h" 17#include "..\Common\DmaData.h" 18#include <vectormath/c/vectormath_aos_v.h> 19 20 21 22// #define SPU_HEAPSIZE (128*1024) 23// #define SPU_STACKSIZE (16*1024) 24// 25// CELL_SPU_LS_PARAM(128*1024, 16*1024); // can't use #defines here as it seems to create an asm instruction 26 27 28static const bool sc_verbose = false; 29 30CellSpursJobContext2* g_pSpursJobContext; 31 32 33// The flag definitions 34static const int CHUNK_FLAG_COMPILED = 0x01; 35static const int CHUNK_FLAG_DIRTY = 0x02; 36static const int CHUNK_FLAG_EMPTY0 = 0x04; 37static const int CHUNK_FLAG_EMPTY1 = 0x08; 38static const int CHUNK_FLAG_EMPTYBOTH = 0x0c; 39static const int CHUNK_FLAG_NOTSKYLIT = 0x10; 40static const int CHUNK_FLAG_REF_MASK = 0x07; 41static const int CHUNK_FLAG_REF_SHIFT = 5; 42 43 44bool inline clip(float *bb, float *frustum) 45{ 46 for (int i = 0; i < 6; ++i, frustum += 4) 47 { 48 if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 49 if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 50 if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 51 if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 52 if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 53 if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 54 if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 55 if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 56 return false; 57 } 58 return true; 59} 60 61class PPUStoreArray 62{ 63 static const int sc_cacheSize = 128; 64 int m_localCache[128]; 65 int* m_pDataPPU; 66 int m_cachePos; 67 int m_ppuPos; 68 69public: 70 PPUStoreArray(uintptr_t pDataPPU) { m_pDataPPU = (int*)pDataPPU; m_cachePos = 0; m_ppuPos = 0;} 71 72 void store(int val) 73 { 74 m_localCache[m_cachePos] = val; 75 m_cachePos++; 76 if(m_cachePos >= sc_cacheSize) 77 flush(); 78 } 79 80 void flush() 81 { 82 if(m_cachePos > 0) 83 { 84 // dma the local cache back to PPU and start again 85// spu_print("DMAing %d bytes from 0x%08x(SPU) to 0x%08x(PPU)\n",(int)( m_cachePos*sizeof(int)), (int)m_localCache, (int)&m_pDataPPU[m_ppuPos]); 86 DmaData_SPU::put(m_localCache, (uintptr_t)&m_pDataPPU[m_ppuPos], DmaData_SPU::roundUpDMASize(m_cachePos*sizeof(int))); 87 m_ppuPos += m_cachePos; 88 m_cachePos = 0; 89 } 90 } 91 int getSize() { return m_ppuPos; } 92}; 93 94 95bool LevelRenderer_FindNearestChunk_DataIn::MultiplayerChunkCache::getChunkEmpty(int lowerOffset, int upperOffset, int x, int y, int z) 96{ 97 x>>=4; 98 z>>=4; 99 int ix = x + XZOFFSET; 100 int iz = z + XZOFFSET; 101 // Check we're in range of the stored level 102 if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return false; // ( waterChunk ? waterChunk : emptyChunk ); 103 if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return false; //( waterChunk ? waterChunk : emptyChunk ); 104 int idx = ix * XZSIZE + iz; 105 106// spu_print("grabbing pointer idx %d from 0x%08x", idx, (uintptr_t)&cache[idx]); 107 uint32_t chunkPointer = DmaData_SPU::getValue32((uintptr_t)&cache[idx]); 108// spu_print(" value - 0x%08x\n", chunkPointer); 109 110 if( chunkPointer == NULL ) 111 { 112 return false; 113 } 114 else 115 { 116 CompressedTileStorage blocks; 117 uintptr_t pBlocks; 118 // using a class structure offset here as we don't want to be compiling LevelChunk on SPU 119 int chunkY = y; 120 if( y >= 128 ) 121 { 122 pBlocks = DmaData_SPU::getValue32((uintptr_t)(chunkPointer+upperOffset)); 123 chunkY -= 128; 124 } 125 else 126 { 127 pBlocks = DmaData_SPU::getValue32((uintptr_t)(chunkPointer+lowerOffset)); 128 } 129 DmaData_SPU::getAndWaitUnaligned(&blocks, pBlocks, sizeof(CompressedTileStorage)); 130 return blocks.isRenderChunkEmpty(chunkY); 131 } 132} 133 134 135bool LevelRenderer_FindNearestChunk_DataIn::CompressedTileStorage::isRenderChunkEmpty(int y) // y == 0, 16, 32... 112 (representing a 16 byte range) 136{ 137 int blockIdx; 138 unsigned short *blockIndices = (unsigned short *)indicesAndData; 139 140 for( int x = 0; x < 16; x += 4 ) 141 { 142 for( int z = 0; z < 16; z += 4 ) 143 { 144 getBlock(&blockIdx, x, y, z); 145 uint16_t comp; 146 comp = DmaData_SPU::getValue16((uintptr_t)&blockIndices[blockIdx]); 147 if( comp != 0x0007 ) return false; 148 comp = DmaData_SPU::getValue16((uintptr_t)&blockIndices[blockIdx+1]); 149 if( comp != 0x0007 ) return false; 150 comp = DmaData_SPU::getValue16((uintptr_t)&blockIndices[blockIdx+2]); 151 if( comp != 0x0007 ) return false; 152 comp = DmaData_SPU::getValue16((uintptr_t)&blockIndices[blockIdx+3]); 153 if( comp != 0x0007 ) return false; 154 } 155 } 156 return true; 157} 158 159 160void LevelRenderer_FindNearestChunk_DataIn::findNearestChunk() 161{ 162 unsigned char* globalChunkFlags = (unsigned char*)alloca(numGlobalChunks); // 164K !!! 163 DmaData_SPU::getAndWait(globalChunkFlags, (uintptr_t)pGlobalChunkFlags, sizeof(unsigned char)*numGlobalChunks); 164 165 166 nearChunk = NULL; // Nearest chunk that is dirty 167 veryNearCount = 0; 168 int minDistSq = 0x7fffffff; // Distances to this chunk 169 170 171 // Find nearest chunk that is dirty 172 for( int p = 0; p < 4; p++ ) 173 { 174 // It's possible that the localplayers member can be set to NULL on the main thread when a player chooses to exit the game 175 // So take a reference to the player object now. As it is a shared_ptr it should live as long as we need it 176 PlayerData* player = &playerData[p]; 177 if( player->bValid == NULL ) continue; 178 if( chunks[p] == NULL ) continue; 179 if( level[p] == NULL ) continue; 180 if( chunkLengths[p] != xChunks * zChunks * CHUNK_Y_COUNT ) continue; 181 int px = (int)player->x; 182 int py = (int)player->y; 183 int pz = (int)player->z; 184 185 ClipChunk clipChunk[512]; 186 187 for( int z = 0; z < zChunks; z++ ) 188 { 189 uintptr_t ClipChunkX_PPU = (uintptr_t)&chunks[p][(z * yChunks + 0) * xChunks + 0]; 190 DmaData_SPU::getAndWait(&clipChunk[0], ClipChunkX_PPU, sizeof(ClipChunk) * xChunks*CHUNK_Y_COUNT); 191 for( int y = 0; y < CHUNK_Y_COUNT; y++ ) 192 { 193 for( int x = 0; x < xChunks; x++ ) 194 { 195 ClipChunk *pClipChunk = &clipChunk[(y) * xChunks + x]; 196 197 // Get distance to this chunk - deliberately not calling the chunk's method of doing this to avoid overheads (passing entitie, type conversion etc.) that this involves 198 int xd = pClipChunk->xm - px; 199 int yd = pClipChunk->ym - py; 200 int zd = pClipChunk->zm - pz; 201 int distSq = xd * xd + yd * yd + zd * zd; 202 int distSqWeighted = xd * xd + yd * yd * 4 + zd * zd; // Weighting against y to prioritise things in same x/z plane as player first 203 204 if( globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_DIRTY ) 205 { 206 if( (!onlyRebuild) || 207 globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED || 208 ( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data 209 { 210 // Is this chunk nearer than our nearest? 211 if( distSqWeighted < minDistSq ) 212 { 213 // At this point we've got a chunk that we would like to consider for rendering, at least based on its proximity to the player(s). 214 // Its *quite* quick to generate empty render data for render chunks, but if we let the rebuilding do that then the after rebuilding we will have 215 // to start searching for the next nearest chunk from scratch again. Instead, its better to detect empty chunks at this stage, flag them up as not dirty 216 // (and empty), and carry on. The levelchunk's isRenderChunkEmpty method can be quite optimal as it can make use of the chunk's data compression to detect 217 // emptiness without actually testing as many data items as uncompressed data would. 218 Chunk chunk; 219 DmaData_SPU::getAndWait(&chunk, (uintptr_t)pClipChunk->chunk, sizeof(Chunk)); 220 if(!multiplayerChunkCache[p].getChunkEmpty(lowerOffset, upperOffset, chunk.x, y*16, chunk.z)) 221 { 222 uintptr_t ClipChunkPPU = (uintptr_t)&chunks[p][(z * yChunks + y) * xChunks + x]; 223 nearChunk = (ClipChunk*)ClipChunkPPU; 224 minDistSq = distSqWeighted; 225 } 226 else 227 { 228 globalChunkFlags[ pClipChunk->globalIdx ] &= ~CHUNK_FLAG_DIRTY; 229 globalChunkFlags[ pClipChunk->globalIdx ] |= CHUNK_FLAG_EMPTYBOTH; 230 } 231 } 232 233 if( distSq < 20 * 20 ) 234 { 235 veryNearCount++; 236 } 237 } 238 } 239 } 240 } 241 } 242 } 243 244 DmaData_SPU::putAndWait(globalChunkFlags, (uintptr_t)pGlobalChunkFlags, sizeof(unsigned char)*numGlobalChunks); 245 246} 247 248 249 250 251void cellSpursJobQueueMain(CellSpursJobContext2 *pContext, CellSpursJob256 *pJob) 252{ 253 // CellSpursTaskId idTask = cellSpursGetTaskId(); 254 unsigned int idSpu = cellSpursGetCurrentSpuId(); 255 256 if(sc_verbose) 257 spu_print("LevelRenderer_cull [SPU#%u] start\n", idSpu); 258 259 g_pSpursJobContext = pContext; 260 uint32_t eaDataIn = pJob->workArea.userData[0]; 261// uint32_t eaDataOut =pJob->workArea.userData[1]; 262 263 LevelRenderer_FindNearestChunk_DataIn dataIn; 264 DmaData_SPU::getAndWait(&dataIn, eaDataIn, sizeof(LevelRenderer_FindNearestChunk_DataIn)); 265 266 dataIn.findNearestChunk(); 267 268 DmaData_SPU::putAndWait(&dataIn, eaDataIn, sizeof(LevelRenderer_FindNearestChunk_DataIn)); 269 270 271 if(sc_verbose) 272 spu_print("LevelRenderer_cull [SPU#%u] exit\n", idSpu); 273} 274