1 // Copyright (c) 2012- PPSSPP Project. 2 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, version 2.0 or later versions. 6 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License 2.0 for more details. 11 12 // A copy of the GPL 2.0 should have been included with the program. 13 // If not, see http://www.gnu.org/licenses/ 14 15 // Official git repository and contact information can be found at 16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. 17 18 #include <algorithm> 19 20 #include "Common/Log.h" 21 #include "Common/MemoryUtil.h" 22 #include "Common/TimeUtil.h" 23 #include "Core/MemMap.h" 24 #include "Core/System.h" 25 #include "Core/Reporting.h" 26 #include "Core/Config.h" 27 #include "Core/CoreTiming.h" 28 29 #include "Common/GPU/D3D9/D3D9StateCache.h" 30 31 #include "GPU/Math3D.h" 32 #include "GPU/GPUState.h" 33 #include "GPU/ge_constants.h" 34 35 #include "GPU/Common/SplineCommon.h" 36 #include "GPU/Common/TransformCommon.h" 37 #include "GPU/Common/VertexDecoderCommon.h" 38 #include "GPU/Common/SoftwareTransformCommon.h" 39 #include "GPU/Debugger/Debugger.h" 40 #include "GPU/Directx9/TextureCacheDX9.h" 41 #include "GPU/Directx9/DrawEngineDX9.h" 42 #include "GPU/Directx9/ShaderManagerDX9.h" 43 #include "GPU/Directx9/GPU_DX9.h" 44 45 namespace DX9 { 46 47 static const D3DPRIMITIVETYPE d3d_prim[8] = { GetDepalettizeVertexShader()48 D3DPT_POINTLIST, 49 D3DPT_LINELIST, 50 D3DPT_LINESTRIP, 51 D3DPT_TRIANGLELIST, 52 D3DPT_TRIANGLESTRIP, 53 D3DPT_TRIANGLEFAN, 54 D3DPT_TRIANGLELIST, 55 }; 56 57 static const int D3DPRIMITIVEVERTEXCOUNT[8][2] = { 58 {0, 0}, // invalid 59 {1, 0}, // 1 = D3DPT_POINTLIST, 60 {2, 0}, // 2 = D3DPT_LINELIST, 61 {2, 1}, // 3 = D3DPT_LINESTRIP, 62 {3, 0}, // 4 = D3DPT_TRIANGLELIST, 63 {1, 2}, // 5 = D3DPT_TRIANGLESTRIP, 64 {1, 2}, // 6 = D3DPT_TRIANGLEFAN, 65 }; 66 67 inline int D3DPrimCount(D3DPRIMITIVETYPE prim, int size) { 68 return (size / D3DPRIMITIVEVERTEXCOUNT[prim][0]) - D3DPRIMITIVEVERTEXCOUNT[prim][1]; 69 } 70 71 enum { 72 TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex) 73 }; 74 75 #define VERTEXCACHE_DECIMATION_INTERVAL 17 76 77 enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; 78 79 static const D3DVERTEXELEMENT9 TransformedVertexElements[] = { 80 { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, 81 { 0, 16, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, 82 { 0, 28, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, 83 { 0, 32, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1 }, 84 D3DDECL_END() 85 }; 86 87 DrawEngineDX9::DrawEngineDX9(Draw::DrawContext *draw) : draw_(draw), vai_(256), vertexDeclMap_(64) { 88 device_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE); 89 decOptions_.expandAllWeightsToFloat = true; 90 decOptions_.expand8BitNormalsToFloat = true; 91 92 decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL; 93 // Allocate nicely aligned memory. Maybe graphics drivers will 94 // appreciate it. 95 // All this is a LOT of memory, need to see if we can cut down somehow. 96 decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); 97 decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); 98 99 indexGen.Setup(decIndex); 100 101 InitDeviceObjects(); 102 103 tessDataTransferDX9 = new TessellationDataTransferDX9(); 104 tessDataTransfer = tessDataTransferDX9; 105 106 device_->CreateVertexDeclaration(TransformedVertexElements, &transformedVertexDecl_); 107 } 108 109 DrawEngineDX9::~DrawEngineDX9() { 110 if (transformedVertexDecl_) { 111 transformedVertexDecl_->Release(); 112 } 113 114 DestroyDeviceObjects(); 115 FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE); 116 FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE); 117 vertexDeclMap_.Iterate([&](const uint32_t &key, IDirect3DVertexDeclaration9 *decl) { 118 if (decl) { 119 decl->Release(); 120 } 121 }); 122 vertexDeclMap_.Clear(); 123 delete tessDataTransferDX9; 124 } 125 126 void DrawEngineDX9::InitDeviceObjects() { 127 128 } 129 130 void DrawEngineDX9::DestroyDeviceObjects() { 131 ClearTrackedVertexArrays(); 132 } 133 134 struct DeclTypeInfo { 135 u32 type; 136 const char * name; 137 }; 138 139 static const DeclTypeInfo VComp[] = { 140 { 0, "NULL" }, // DEC_NONE, 141 { D3DDECLTYPE_FLOAT1, "D3DDECLTYPE_FLOAT1 " }, // DEC_FLOAT_1, 142 { D3DDECLTYPE_FLOAT2, "D3DDECLTYPE_FLOAT2 " }, // DEC_FLOAT_2, 143 { D3DDECLTYPE_FLOAT3, "D3DDECLTYPE_FLOAT3 " }, // DEC_FLOAT_3, 144 { D3DDECLTYPE_FLOAT4, "D3DDECLTYPE_FLOAT4 " }, // DEC_FLOAT_4, 145 146 { 0, "UNUSED" }, // DEC_S8_3, 147 148 { D3DDECLTYPE_SHORT4N, "D3DDECLTYPE_SHORT4N " }, // DEC_S16_3, 149 { D3DDECLTYPE_UBYTE4N, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_1, 150 { D3DDECLTYPE_UBYTE4N, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_2, 151 { D3DDECLTYPE_UBYTE4N, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_3, 152 { D3DDECLTYPE_UBYTE4N, "D3DDECLTYPE_UBYTE4N " }, // DEC_U8_4, 153 {0, "UNUSED_DEC_U16_1" }, // DEC_U16_1, 154 {0, "UNUSED_DEC_U16_2" }, // DEC_U16_2, 155 {D3DDECLTYPE_USHORT4N ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_3, 156 {D3DDECLTYPE_USHORT4N ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_4, 157 }; 158 159 static void VertexAttribSetup(D3DVERTEXELEMENT9 * VertexElement, u8 fmt, u8 offset, u8 usage, u8 usage_index = 0) { 160 memset(VertexElement, 0, sizeof(D3DVERTEXELEMENT9)); 161 VertexElement->Offset = offset; 162 VertexElement->Type = VComp[fmt].type; 163 VertexElement->Usage = usage; 164 VertexElement->UsageIndex = usage_index; 165 } 166 167 IDirect3DVertexDeclaration9 *DrawEngineDX9::SetupDecFmtForDraw(VSShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt) { 168 IDirect3DVertexDeclaration9 *vertexDeclCached = vertexDeclMap_.Get(pspFmt); 169 170 if (vertexDeclCached) { 171 return vertexDeclCached; 172 } else { 173 D3DVERTEXELEMENT9 VertexElements[8]; 174 D3DVERTEXELEMENT9 *VertexElement = &VertexElements[0]; 175 176 // Vertices Elements orders 177 // WEIGHT 178 if (decFmt.w0fmt != 0) { 179 VertexAttribSetup(VertexElement, decFmt.w0fmt, decFmt.w0off, D3DDECLUSAGE_TEXCOORD, 1); 180 VertexElement++; 181 } 182 183 if (decFmt.w1fmt != 0) { 184 VertexAttribSetup(VertexElement, decFmt.w1fmt, decFmt.w1off, D3DDECLUSAGE_TEXCOORD, 2); 185 VertexElement++; 186 } 187 188 // TC 189 if (decFmt.uvfmt != 0) { 190 VertexAttribSetup(VertexElement, decFmt.uvfmt, decFmt.uvoff, D3DDECLUSAGE_TEXCOORD, 0); 191 VertexElement++; 192 } 193 194 // COLOR 195 if (decFmt.c0fmt != 0) { 196 VertexAttribSetup(VertexElement, decFmt.c0fmt, decFmt.c0off, D3DDECLUSAGE_COLOR, 0); 197 VertexElement++; 198 } 199 // Never used ? 200 if (decFmt.c1fmt != 0) { 201 VertexAttribSetup(VertexElement, decFmt.c1fmt, decFmt.c1off, D3DDECLUSAGE_COLOR, 1); 202 VertexElement++; 203 } 204 205 // NORMAL 206 if (decFmt.nrmfmt != 0) { 207 VertexAttribSetup(VertexElement, decFmt.nrmfmt, decFmt.nrmoff, D3DDECLUSAGE_NORMAL, 0); 208 VertexElement++; 209 } 210 211 // POSITION 212 // Always 213 VertexAttribSetup(VertexElement, decFmt.posfmt, decFmt.posoff, D3DDECLUSAGE_POSITION, 0); 214 VertexElement++; 215 216 // End 217 D3DVERTEXELEMENT9 end = D3DDECL_END(); 218 memcpy(VertexElement, &end, sizeof(D3DVERTEXELEMENT9)); 219 220 // Create declaration 221 IDirect3DVertexDeclaration9 *pHardwareVertexDecl = nullptr; 222 HRESULT hr = device_->CreateVertexDeclaration( VertexElements, &pHardwareVertexDecl ); 223 if (FAILED(hr)) { 224 ERROR_LOG(G3D, "Failed to create vertex declaration!"); 225 pHardwareVertexDecl = nullptr; 226 } 227 228 // Add it to map 229 vertexDeclMap_.Insert(pspFmt, pHardwareVertexDecl); 230 return pHardwareVertexDecl; 231 } 232 } 233 234 void DrawEngineDX9::MarkUnreliable(VertexArrayInfoDX9 *vai) { 235 vai->status = VertexArrayInfoDX9::VAI_UNRELIABLE; 236 if (vai->vbo) { 237 vai->vbo->Release(); 238 vai->vbo = nullptr; 239 } 240 if (vai->ebo) { 241 vai->ebo->Release(); 242 vai->ebo = nullptr; 243 } 244 } 245 246 void DrawEngineDX9::ClearTrackedVertexArrays() { 247 vai_.Iterate([&](uint32_t hash, DX9::VertexArrayInfoDX9 *vai) { 248 delete vai; 249 }); 250 vai_.Clear(); 251 } 252 253 void DrawEngineDX9::DecimateTrackedVertexArrays() { 254 if (--decimationCounter_ <= 0) { 255 decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL; 256 } else { 257 return; 258 } 259 260 const int threshold = gpuStats.numFlips - VAI_KILL_AGE; 261 const int unreliableThreshold = gpuStats.numFlips - VAI_UNRELIABLE_KILL_AGE; 262 int unreliableLeft = VAI_UNRELIABLE_KILL_MAX; 263 vai_.Iterate([&](uint32_t hash, DX9::VertexArrayInfoDX9 *vai) { 264 bool kill; 265 if (vai->status == VertexArrayInfoDX9::VAI_UNRELIABLE) { 266 // We limit killing unreliable so we don't rehash too often. 267 kill = vai->lastFrame < unreliableThreshold && --unreliableLeft >= 0; 268 } else { 269 kill = vai->lastFrame < threshold; 270 } 271 if (kill) { 272 delete vai; 273 vai_.Remove(hash); 274 } 275 }); 276 vai_.Maintain(); 277 278 // Enable if you want to see vertex decoders in the log output. Need a better way. 279 #if 0 280 char buffer[16384]; 281 for (std::map<u32, VertexDecoder*>::iterator dec = decoderMap_.begin(); dec != decoderMap_.end(); ++dec) { 282 char *ptr = buffer; 283 ptr += dec->second->ToString(ptr); 284 // *ptr++ = '\n'; 285 NOTICE_LOG(G3D, buffer); 286 } 287 #endif 288 } 289 290 VertexArrayInfoDX9::~VertexArrayInfoDX9() { 291 if (vbo) { 292 vbo->Release(); 293 } 294 if (ebo) { 295 ebo->Release(); 296 } 297 } 298 299 static uint32_t SwapRB(uint32_t c) { 300 return (c & 0xFF00FF00) | ((c >> 16) & 0xFF) | ((c << 16) & 0xFF0000); 301 } 302 303 void DrawEngineDX9::BeginFrame() { 304 DecimateTrackedVertexArrays(); 305 306 lastRenderStepId_ = -1; 307 } 308 309 // The inline wrapper in the header checks for numDrawCalls == 0 310 void DrawEngineDX9::DoFlush() { 311 gpuStats.numFlushes++; 312 gpuStats.numTrackedVertexArrays = (int)vai_.size(); 313 314 // In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. 315 int curRenderStepId = draw_->GetCurrentStepId(); 316 if (lastRenderStepId_ != curRenderStepId) { 317 // Dirty everything that has dynamic state that will need re-recording. 318 gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); 319 lastRenderStepId_ = curRenderStepId; 320 } 321 322 // This is not done on every drawcall, we should collect vertex data 323 // until critical state changes. That's when we draw (flush). 324 GEPrimitiveType prim = prevPrim_; 325 ApplyDrawState(prim); 326 327 VSShader *vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); 328 329 if (vshader->UseHWTransform()) { 330 LPDIRECT3DVERTEXBUFFER9 vb_ = NULL; 331 LPDIRECT3DINDEXBUFFER9 ib_ = NULL; 332 333 int vertexCount = 0; 334 int maxIndex = 0; 335 bool useElements = true; 336 337 // Cannot cache vertex data with morph enabled. 338 bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); 339 // Also avoid caching when software skinning. 340 if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) 341 useCache = false; 342 343 if (useCache) { 344 u32 id = dcid_ ^ gstate.getUVGenMode(); // This can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263 345 VertexArrayInfoDX9 *vai = vai_.Get(id); 346 if (!vai) { 347 vai = new VertexArrayInfoDX9(); 348 vai_.Insert(id, vai); 349 } 350 351 switch (vai->status) { 352 case VertexArrayInfoDX9::VAI_NEW: 353 { 354 // Haven't seen this one before. 355 uint64_t dataHash = ComputeHash(); 356 vai->hash = dataHash; 357 vai->minihash = ComputeMiniHash(); 358 vai->status = VertexArrayInfoDX9::VAI_HASHING; 359 vai->drawsUntilNextFullHash = 0; 360 DecodeVerts(decoded); // writes to indexGen 361 vai->numVerts = indexGen.VertexCount(); 362 vai->prim = indexGen.Prim(); 363 vai->maxIndex = indexGen.MaxIndex(); 364 vai->flags = gstate_c.vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0; 365 366 goto rotateVBO; 367 } 368 369 // Hashing - still gaining confidence about the buffer. 370 // But if we get this far it's likely to be worth creating a vertex buffer. 371 case VertexArrayInfoDX9::VAI_HASHING: 372 { 373 vai->numDraws++; 374 if (vai->lastFrame != gpuStats.numFlips) { 375 vai->numFrames++; 376 } 377 if (vai->drawsUntilNextFullHash == 0) { 378 // Let's try to skip a full hash if mini would fail. 379 const u32 newMiniHash = ComputeMiniHash(); 380 uint64_t newHash = vai->hash; 381 if (newMiniHash == vai->minihash) { 382 newHash = ComputeHash(); 383 } 384 if (newMiniHash != vai->minihash || newHash != vai->hash) { 385 MarkUnreliable(vai); 386 DecodeVerts(decoded); 387 goto rotateVBO; 388 } 389 if (vai->numVerts > 64) { 390 // exponential backoff up to 16 draws, then every 24 391 vai->drawsUntilNextFullHash = std::min(24, vai->numFrames); 392 } else { 393 // Lower numbers seem much more likely to change. 394 vai->drawsUntilNextFullHash = 0; 395 } 396 // TODO: tweak 397 //if (vai->numFrames > 1000) { 398 // vai->status = VertexArrayInfo::VAI_RELIABLE; 399 //} 400 } else { 401 vai->drawsUntilNextFullHash--; 402 u32 newMiniHash = ComputeMiniHash(); 403 if (newMiniHash != vai->minihash) { 404 MarkUnreliable(vai); 405 DecodeVerts(decoded); 406 goto rotateVBO; 407 } 408 } 409 410 if (vai->vbo == 0) { 411 DecodeVerts(decoded); 412 vai->numVerts = indexGen.VertexCount(); 413 vai->prim = indexGen.Prim(); 414 vai->maxIndex = indexGen.MaxIndex(); 415 vai->flags = gstate_c.vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0; 416 useElements = !indexGen.SeenOnlyPurePrims(); 417 if (!useElements && indexGen.PureCount()) { 418 vai->numVerts = indexGen.PureCount(); 419 } 420 421 _dbg_assert_msg_(gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching."); 422 423 void * pVb; 424 u32 size = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex(); 425 device_->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &vai->vbo, NULL); 426 vai->vbo->Lock(0, size, &pVb, 0); 427 memcpy(pVb, decoded, size); 428 vai->vbo->Unlock(); 429 if (useElements) { 430 void * pIb; 431 u32 size = sizeof(short) * indexGen.VertexCount(); 432 device_->CreateIndexBuffer(size, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &vai->ebo, NULL); 433 vai->ebo->Lock(0, size, &pIb, 0); 434 memcpy(pIb, decIndex, size); 435 vai->ebo->Unlock(); 436 } else { 437 vai->ebo = 0; 438 } 439 } else { 440 gpuStats.numCachedDrawCalls++; 441 useElements = vai->ebo ? true : false; 442 gpuStats.numCachedVertsDrawn += vai->numVerts; 443 gstate_c.vertexFullAlpha = vai->flags & VAI_FLAG_VERTEXFULLALPHA; 444 } 445 vb_ = vai->vbo; 446 ib_ = vai->ebo; 447 vertexCount = vai->numVerts; 448 maxIndex = vai->maxIndex; 449 prim = static_cast<GEPrimitiveType>(vai->prim); 450 break; 451 } 452 453 // Reliable - we don't even bother hashing anymore. Right now we don't go here until after a very long time. 454 case VertexArrayInfoDX9::VAI_RELIABLE: 455 { 456 vai->numDraws++; 457 if (vai->lastFrame != gpuStats.numFlips) { 458 vai->numFrames++; 459 } 460 gpuStats.numCachedDrawCalls++; 461 gpuStats.numCachedVertsDrawn += vai->numVerts; 462 vb_ = vai->vbo; 463 ib_ = vai->ebo; 464 465 vertexCount = vai->numVerts; 466 467 maxIndex = vai->maxIndex; 468 prim = static_cast<GEPrimitiveType>(vai->prim); 469 470 gstate_c.vertexFullAlpha = vai->flags & VAI_FLAG_VERTEXFULLALPHA; 471 break; 472 } 473 474 case VertexArrayInfoDX9::VAI_UNRELIABLE: 475 { 476 vai->numDraws++; 477 if (vai->lastFrame != gpuStats.numFlips) { 478 vai->numFrames++; 479 } 480 DecodeVerts(decoded); 481 goto rotateVBO; 482 } 483 } 484 485 vai->lastFrame = gpuStats.numFlips; 486 } else { 487 DecodeVerts(decoded); 488 rotateVBO: 489 gpuStats.numUncachedVertsDrawn += indexGen.VertexCount(); 490 useElements = !indexGen.SeenOnlyPurePrims(); 491 vertexCount = indexGen.VertexCount(); 492 maxIndex = indexGen.MaxIndex(); 493 if (!useElements && indexGen.PureCount()) { 494 vertexCount = indexGen.PureCount(); 495 } 496 prim = indexGen.Prim(); 497 } 498 499 VERBOSE_LOG(G3D, "Flush prim %i! %i verts in one go", prim, vertexCount); 500 bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; 501 if (gstate.isModeThrough()) { 502 gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); 503 } else { 504 gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); 505 } 506 507 ApplyDrawStateLate(); 508 vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); 509 IDirect3DVertexDeclaration9 *pHardwareVertexDecl = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); 510 511 if (pHardwareVertexDecl) { 512 device_->SetVertexDeclaration(pHardwareVertexDecl); 513 if (vb_ == NULL) { 514 if (useElements) { 515 device_->DrawIndexedPrimitiveUP(d3d_prim[prim], 0, maxIndex + 1, D3DPrimCount(d3d_prim[prim], vertexCount), decIndex, D3DFMT_INDEX16, decoded, dec_->GetDecVtxFmt().stride); 516 } else { 517 device_->DrawPrimitiveUP(d3d_prim[prim], D3DPrimCount(d3d_prim[prim], vertexCount), decoded, dec_->GetDecVtxFmt().stride); 518 } 519 } else { 520 device_->SetStreamSource(0, vb_, 0, dec_->GetDecVtxFmt().stride); 521 522 if (useElements) { 523 device_->SetIndices(ib_); 524 525 device_->DrawIndexedPrimitive(d3d_prim[prim], 0, 0, maxIndex + 1, 0, D3DPrimCount(d3d_prim[prim], vertexCount)); 526 } else { 527 device_->DrawPrimitive(d3d_prim[prim], 0, D3DPrimCount(d3d_prim[prim], vertexCount)); 528 } 529 } 530 } 531 } else { 532 DecodeVerts(decoded); 533 bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; 534 if (gstate.isModeThrough()) { 535 gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); 536 } else { 537 gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); 538 } 539 540 gpuStats.numUncachedVertsDrawn += indexGen.VertexCount(); 541 prim = indexGen.Prim(); 542 // Undo the strip optimization, not supported by the SW code yet. 543 if (prim == GE_PRIM_TRIANGLE_STRIP) 544 prim = GE_PRIM_TRIANGLES; 545 VERBOSE_LOG(G3D, "Flush prim %i SW! %i verts in one go", prim, indexGen.VertexCount()); 546 547 u16 *inds = decIndex; 548 SoftwareTransformResult result{}; 549 SoftwareTransformParams params{}; 550 params.decoded = decoded; 551 params.transformed = transformed; 552 params.transformedExpanded = transformedExpanded; 553 params.fbman = framebufferManager_; 554 params.texCache = textureCache_; 555 params.allowClear = true; 556 params.allowSeparateAlphaClear = false; 557 params.provokeFlatFirst = true; 558 559 int maxIndex = indexGen.MaxIndex(); 560 SoftwareTransform swTransform(params); 561 swTransform.Decode(prim, dec_->VertexType(), dec_->GetDecVtxFmt(), maxIndex, &result); 562 if (result.action == SW_NOT_READY) { 563 swTransform.DetectOffsetTexture(maxIndex); 564 swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result); 565 } 566 567 if (result.setSafeSize) 568 framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); 569 570 ApplyDrawStateLate(); 571 vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat); 572 573 if (result.action == SW_DRAW_PRIMITIVES) { 574 if (result.setStencil) { 575 dxstate.stencilFunc.set(D3DCMP_ALWAYS, result.stencilValue, 255); 576 } 577 578 // TODO: Add a post-transform cache here for multi-RECTANGLES only. 579 // Might help for text drawing. 580 581 device_->SetVertexDeclaration(transformedVertexDecl_); 582 if (result.drawIndexed) { 583 device_->DrawIndexedPrimitiveUP(d3d_prim[prim], 0, maxIndex, D3DPrimCount(d3d_prim[prim], result.drawNumTrans), inds, D3DFMT_INDEX16, result.drawBuffer, sizeof(TransformedVertex)); 584 } else { 585 device_->DrawPrimitiveUP(d3d_prim[prim], D3DPrimCount(d3d_prim[prim], result.drawNumTrans), result.drawBuffer, sizeof(TransformedVertex)); 586 } 587 } else if (result.action == SW_CLEAR) { 588 u32 clearColor = result.color; 589 float clearDepth = result.depth; 590 591 int mask = gstate.isClearModeColorMask() ? D3DCLEAR_TARGET : 0; 592 if (gstate.isClearModeAlphaMask()) mask |= D3DCLEAR_STENCIL; 593 if (gstate.isClearModeDepthMask()) mask |= D3DCLEAR_ZBUFFER; 594 595 if (mask & D3DCLEAR_ZBUFFER) { 596 framebufferManager_->SetDepthUpdated(); 597 } 598 if (mask & D3DCLEAR_TARGET) { 599 framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); 600 } 601 602 device_->Clear(0, NULL, mask, SwapRB(clearColor), clearDepth, clearColor >> 24); 603 604 if ((gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) { 605 int scissorX1 = gstate.getScissorX1(); 606 int scissorY1 = gstate.getScissorY1(); 607 int scissorX2 = gstate.getScissorX2() + 1; 608 int scissorY2 = gstate.getScissorY2() + 1; 609 framebufferManager_->ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, clearColor); 610 } 611 } 612 } 613 614 gpuStats.numDrawCalls += numDrawCalls; 615 gpuStats.numVertsSubmitted += vertexCountInDrawCalls_; 616 617 indexGen.Reset(); 618 decodedVerts_ = 0; 619 numDrawCalls = 0; 620 vertexCountInDrawCalls_ = 0; 621 decodeCounter_ = 0; 622 dcid_ = 0; 623 prevPrim_ = GE_PRIM_INVALID; 624 gstate_c.vertexFullAlpha = true; 625 framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); 626 627 // Now seems as good a time as any to reset the min/max coords, which we may examine later. 628 gstate_c.vertBounds.minU = 512; 629 gstate_c.vertBounds.minV = 512; 630 gstate_c.vertBounds.maxU = 0; 631 gstate_c.vertBounds.maxV = 0; 632 633 GPUDebug::NotifyDraw(); 634 } 635 636 void TessellationDataTransferDX9::SendDataToShader(const SimpleVertex *const *points, int size_u, int size_v, u32 vertType, const Spline::Weight2D &weights) { 637 // TODO 638 } 639 640 } // namespace 641