mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-07 21:23:29 +02:00
ApplyVertexShader can return null - if the requested shader fails to compile it retries with a software transform ID, and if that fails too it returns (and caches) null. We then called UseHWTransform() on it. ApplyFragmentShader can likewise return null, and the hardware path ignored it, unlike the software path. Without a linked shader nothing binds a program for this render pass, so the draw would have gone through with whatever program a previous pass left bound.
457 lines
17 KiB
C++
457 lines
17 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// 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, version 2.0 or later versions.
|
|
|
|
// 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 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Common/LogReporting.h"
|
|
|
|
#include "Common/GPU/OpenGL/GLDebugLog.h"
|
|
#include "Common/Profiler/Profiler.h"
|
|
|
|
#include "GPU/GPUState.h"
|
|
#include "GPU/ge_constants.h"
|
|
|
|
#include "GPU/Common/SplineCommon.h"
|
|
#include "GPU/Common/VertexDecoderCommon.h"
|
|
#include "GPU/Common/SoftwareTransformCommon.h"
|
|
#include "GPU/GLES/DrawEngineGLES.h"
|
|
#include "GPU/GLES/ShaderManagerGLES.h"
|
|
#include "GPU/GLES/GPU_GLES.h"
|
|
#include "GPU/GLES/FramebufferManagerGLES.h"
|
|
|
|
static const GLuint glprim[8] = {
|
|
// Points, which are expanded to triangles.
|
|
GL_TRIANGLES,
|
|
// Lines and line strips, which are also expanded to triangles.
|
|
GL_TRIANGLES,
|
|
GL_TRIANGLES,
|
|
GL_TRIANGLES,
|
|
GL_TRIANGLE_STRIP,
|
|
GL_TRIANGLE_FAN,
|
|
// Rectangles, which are expanded to triangles.
|
|
GL_TRIANGLES,
|
|
};
|
|
|
|
enum {
|
|
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex)
|
|
};
|
|
|
|
DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : inputLayoutMap_(16), draw_(draw) {
|
|
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
|
|
|
decOptions_.expand8BitNormalsToFloat = false;
|
|
|
|
InitDeviceObjects();
|
|
}
|
|
|
|
DrawEngineGLES::~DrawEngineGLES() {
|
|
DestroyDeviceObjects();
|
|
}
|
|
|
|
void DrawEngineGLES::DeviceLost() {
|
|
DestroyDeviceObjects();
|
|
draw_ = nullptr;
|
|
render_ = nullptr;
|
|
}
|
|
|
|
void DrawEngineGLES::DeviceRestore(Draw::DrawContext *draw) {
|
|
draw_ = draw;
|
|
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
|
InitDeviceObjects();
|
|
}
|
|
|
|
void DrawEngineGLES::InitDeviceObjects() {
|
|
_assert_msg_(render_ != nullptr, "Render manager must be set");
|
|
|
|
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
|
frameData_[i].pushVertex = render_->CreatePushBuffer(i, GL_ARRAY_BUFFER, 2 * 1024 * 1024, 256, "game_vertex");
|
|
frameData_[i].pushIndex = render_->CreatePushBuffer(i, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024, 64, "game_index");
|
|
}
|
|
|
|
int stride = sizeof(TransformedVertex);
|
|
std::vector<GLRInputLayout::Entry> entries;
|
|
entries.push_back({ ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, offsetof(TransformedVertex, x) });
|
|
entries.push_back({ ATTR_TEXCOORD, 3, GL_FLOAT, GL_FALSE, offsetof(TransformedVertex, u) });
|
|
entries.push_back({ ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, offsetof(TransformedVertex, color0_32) });
|
|
entries.push_back({ ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, offsetof(TransformedVertex, color1_32) });
|
|
entries.push_back({ ATTR_NORMAL, 1, GL_FLOAT, GL_FALSE, offsetof(TransformedVertex, fog) });
|
|
softwareInputLayout_ = render_->CreateInputLayout(entries, stride);
|
|
|
|
draw_->SetInvalidationCallback(std::bind(&DrawEngineGLES::Invalidate, this, std::placeholders::_1));
|
|
}
|
|
|
|
void DrawEngineGLES::DestroyDeviceObjects() {
|
|
if (!draw_) {
|
|
return;
|
|
}
|
|
draw_->SetInvalidationCallback(InvalidationCallback());
|
|
|
|
// Beware: this could be called twice in a row, sometimes.
|
|
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
|
if (!frameData_[i].pushVertex && !frameData_[i].pushIndex)
|
|
continue;
|
|
|
|
if (frameData_[i].pushVertex)
|
|
render_->DeletePushBuffer(frameData_[i].pushVertex);
|
|
if (frameData_[i].pushIndex)
|
|
render_->DeletePushBuffer(frameData_[i].pushIndex);
|
|
frameData_[i].pushVertex = nullptr;
|
|
frameData_[i].pushIndex = nullptr;
|
|
}
|
|
|
|
if (softwareInputLayout_)
|
|
render_->DeleteInputLayout(softwareInputLayout_);
|
|
softwareInputLayout_ = nullptr;
|
|
|
|
ClearInputLayoutMap();
|
|
}
|
|
|
|
void DrawEngineGLES::ClearInputLayoutMap() {
|
|
inputLayoutMap_.Iterate([&](const uint32_t &key, GLRInputLayout *il) {
|
|
render_->DeleteInputLayout(il);
|
|
});
|
|
inputLayoutMap_.Clear();
|
|
}
|
|
|
|
void DrawEngineGLES::BeginFrame() {
|
|
DrawEngineCommon::BeginFrame();
|
|
|
|
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
|
frameData.pushIndex->Begin();
|
|
frameData.pushVertex->Begin();
|
|
|
|
lastRenderStepId_ = -1;
|
|
}
|
|
|
|
void DrawEngineGLES::EndFrame() {
|
|
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
|
frameData.pushIndex->End();
|
|
frameData.pushVertex->End();
|
|
}
|
|
|
|
struct GlTypeInfo {
|
|
u16 type;
|
|
u8 count;
|
|
u8 normalized;
|
|
};
|
|
|
|
static const GlTypeInfo GLComp[] = {
|
|
{0}, // DEC_NONE,
|
|
{GL_FLOAT, 1, GL_FALSE}, // DEC_FLOAT_1,
|
|
{GL_FLOAT, 2, GL_FALSE}, // DEC_FLOAT_2,
|
|
{GL_FLOAT, 3, GL_FALSE}, // DEC_FLOAT_3,
|
|
{GL_FLOAT, 4, GL_FALSE}, // DEC_FLOAT_4,
|
|
{GL_BYTE, 4, GL_TRUE}, // DEC_S8_3,
|
|
{GL_SHORT, 4, GL_TRUE},// DEC_S16_3,
|
|
{GL_UNSIGNED_BYTE, 1, GL_TRUE},// DEC_U8_1,
|
|
{GL_UNSIGNED_BYTE, 2, GL_TRUE},// DEC_U8_2,
|
|
{GL_UNSIGNED_BYTE, 3, GL_TRUE},// DEC_U8_3,
|
|
{GL_UNSIGNED_BYTE, 4, GL_TRUE},// DEC_U8_4,
|
|
{GL_UNSIGNED_SHORT, 1, GL_TRUE},// DEC_U16_1,
|
|
{GL_UNSIGNED_SHORT, 2, GL_TRUE},// DEC_U16_2,
|
|
{GL_UNSIGNED_SHORT, 3, GL_TRUE},// DEC_U16_3,
|
|
{GL_UNSIGNED_SHORT, 4, GL_TRUE},// DEC_U16_4,
|
|
};
|
|
|
|
static inline void VertexAttribSetup(int attrib, int fmt, int offset, std::vector<GLRInputLayout::Entry> &entries) {
|
|
if (fmt) {
|
|
const GlTypeInfo &type = GLComp[fmt];
|
|
GLRInputLayout::Entry entry;
|
|
entry.offset = offset;
|
|
entry.location = attrib;
|
|
entry.normalized = type.normalized;
|
|
entry.type = type.type;
|
|
entry.count = type.count;
|
|
entries.push_back(entry);
|
|
}
|
|
}
|
|
|
|
// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets
|
|
GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(const DecVtxFormat &decFmt) {
|
|
uint32_t key = decFmt.id;
|
|
GLRInputLayout *inputLayout;
|
|
if (inputLayoutMap_.Get(key, &inputLayout)) {
|
|
return inputLayout;
|
|
}
|
|
|
|
std::vector<GLRInputLayout::Entry> entries;
|
|
VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.w0off, entries);
|
|
VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.w1off, entries);
|
|
VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.uvoff, entries);
|
|
VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.c0off, entries);
|
|
VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.c1off, entries);
|
|
VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.nrmoff, entries);
|
|
VertexAttribSetup(ATTR_POSITION, DecVtxFormat::PosFmt(), decFmt.posoff, entries);
|
|
|
|
int stride = decFmt.stride;
|
|
inputLayout = render_->CreateInputLayout(entries, stride);
|
|
inputLayoutMap_.Insert(key, inputLayout);
|
|
return inputLayout;
|
|
}
|
|
|
|
// A new render step means we need to flush any dynamic state. Really, any state that is reset in
|
|
// GLQueueRunner::PerformRenderPass.
|
|
void DrawEngineGLES::Invalidate(InvalidationCallbackFlags flags) {
|
|
if (flags & InvalidationCallbackFlags::RENDER_PASS_STATE) {
|
|
// Dirty everything that has dynamic state that will need re-recording.
|
|
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
|
}
|
|
}
|
|
|
|
void DrawEngineGLES::Flush() {
|
|
if (!numDrawVerts_) {
|
|
return;
|
|
}
|
|
PROFILE_THIS_SCOPE("flush");
|
|
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
|
VShaderID vsid;
|
|
|
|
if (!render_->IsInRenderPass()) {
|
|
// Something went badly wrong. Try to survive by simply skipping the draw, though.
|
|
_dbg_assert_msg_(false, "Trying to DoFlush while not in a render pass. This is bad.");
|
|
// can't goto bail here, skips too many variable initializations. So let's wipe the most important stuff.
|
|
indexGen.Reset();
|
|
numDecodedVerts_ = 0;
|
|
numDrawVerts_ = 0;
|
|
numDrawInds_ = 0;
|
|
vertexCountInDrawCalls_ = 0;
|
|
decodeVertsCounter_ = 0;
|
|
decodeIndsCounter_ = 0;
|
|
return;
|
|
}
|
|
|
|
GEPrimitiveType prim = prevPrim_;
|
|
|
|
bool useHWTransform = CanUseHardwareTransform(prim);
|
|
if (clipInfoFlags_ & ClipInfoFlags::Valid) {
|
|
if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) {
|
|
useHWTransform = false;
|
|
}
|
|
}
|
|
if (clipInfoFlags_ != lastClipInfoFlags_) {
|
|
ClipInfoFlags changed = (ClipInfoFlags)((u32)clipInfoFlags_ ^ (u32)lastClipInfoFlags_);
|
|
if (changed & (ClipInfoFlags::DepthClampFragment | ClipInfoFlags::MinMaxZDiscard)) {
|
|
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE);
|
|
}
|
|
if (changed & ClipInfoFlags::FlatZ) {
|
|
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
|
}
|
|
lastClipInfoFlags_ = clipInfoFlags_;
|
|
}
|
|
|
|
if (useHWTransform != lastUseHwTransform_) {
|
|
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE);
|
|
lastUseHwTransform_ = useHWTransform;
|
|
}
|
|
|
|
GLRBuffer *vertexBuffer = nullptr;
|
|
GLRBuffer *indexBuffer = nullptr;
|
|
uint32_t vertexBufferOffset = 0;
|
|
uint32_t indexBufferOffset = 0;
|
|
|
|
Shader *vshader = shaderManager_->ApplyVertexShader(useHWTransform, dec_->VertexType(), clipInfoFlags_, &vsid);
|
|
if (!vshader) {
|
|
// Both the requested shader and the software transform fallback failed to compile.
|
|
// Not much we can do here, let's skip drawing.
|
|
goto bail;
|
|
}
|
|
|
|
useHWTransform = vshader->UseHWTransform(); // In case shader compilation failed and it fell back.
|
|
|
|
if (useHWTransform) {
|
|
if (lastVType_ & GE_VTYPE_WEIGHT_MASK) {
|
|
// If software skinning, we're predecoding into "decoded". So make sure we're done, then push that content.
|
|
DecodeVerts(dec_, decoded_);
|
|
uint32_t size = numDecodedVerts_ * dec_->GetDecVtxFmt().stride;
|
|
u8 *dest = (u8 *)frameData.pushVertex->Allocate(size, 4, &vertexBuffer, &vertexBufferOffset);
|
|
memcpy(dest, decoded_, size);
|
|
} else {
|
|
// Figure out how much pushbuffer space we need to allocate.
|
|
int vertsToDecode = ComputeNumVertsToDecode();
|
|
u8 *dest = (u8 *)frameData.pushVertex->Allocate(vertsToDecode * dec_->GetDecVtxFmt().stride, 4, &vertexBuffer, &vertexBufferOffset);
|
|
DecodeVerts(dec_, dest);
|
|
}
|
|
|
|
int vertexCount;
|
|
int maxIndex;
|
|
bool useElements;
|
|
DecodeVerts(dec_, decoded_);
|
|
DecodeIndsAndGetData(&prim, &vertexCount, &maxIndex, &useElements, false);
|
|
gpuStats.perFrame.numVertsDrawn += vertexCount;
|
|
|
|
if (useElements) {
|
|
uint32_t esz = sizeof(uint16_t) * vertexCount;
|
|
void *dest = frameData.pushIndex->Allocate(esz, 2, &indexBuffer, &indexBufferOffset);
|
|
// TODO: When we need to apply an index offset, we can apply it directly when copying the indices here.
|
|
// Of course, minding the maximum value of 65535...
|
|
memcpy(dest, decIndex_, esz);
|
|
}
|
|
|
|
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
|
|
if (gstate.isModeThrough()) {
|
|
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
|
|
} else {
|
|
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
|
|
}
|
|
|
|
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
|
|
TextureApplyResult textureResult = textureCache_->ApplyTexture(true);
|
|
textureCache_->ApplySampler(textureResult, clipInfoFlags_ & ClipInfoFlags::FlatZ, false);
|
|
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
|
} else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) {
|
|
// This catches the case of clearing a texture. (#10957)
|
|
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
|
}
|
|
|
|
// Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state.
|
|
ApplyDrawState(prim);
|
|
ApplyDrawStateLate(false, 0);
|
|
|
|
LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, clipInfoFlags_, false);
|
|
if (!program) {
|
|
// Failed to link. No program is bound, so drawing would use whatever was bound before.
|
|
goto bail;
|
|
}
|
|
|
|
GLRInputLayout *inputLayout = SetupDecFmtForDraw(dec_->GetDecVtxFmt());
|
|
if (useElements) {
|
|
render_->DrawIndexed(inputLayout,
|
|
vertexBuffer, vertexBufferOffset,
|
|
indexBuffer, indexBufferOffset,
|
|
glprim[prim], vertexCount, GL_UNSIGNED_SHORT);
|
|
} else {
|
|
render_->Draw(
|
|
inputLayout, vertexBuffer, vertexBufferOffset,
|
|
glprim[prim], 0, vertexCount);
|
|
}
|
|
if (useDepthRaster_) {
|
|
DepthRasterSubmitRaw(prim, dec_, dec_->VertexType(), vertexCount);
|
|
}
|
|
} else {
|
|
PROFILE_THIS_SCOPE("soft");
|
|
DecodeVerts(dec_, decoded_);
|
|
int vertexCount = DecodeInds();
|
|
|
|
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
|
|
if (gstate.isModeThrough()) {
|
|
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
|
|
} else {
|
|
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
|
|
}
|
|
|
|
prim = IndexGenerator::GeneralPrim((GEPrimitiveType)drawInds_[0].prim);
|
|
|
|
int maxIndex = numDecodedVerts_;
|
|
|
|
// TODO: Split up into multiple draw calls for GLES 2.0 where you can't guarantee support for more than 0x10000 verts.
|
|
if (gl_extensions.IsGLES && !gl_extensions.GLES3) {
|
|
constexpr int vertexCountLimit = 0x10000 / 3;
|
|
if (vertexCount > vertexCountLimit) {
|
|
WARN_LOG_REPORT_ONCE(manyVerts, Log::G3D, "Truncating vertex count from %d to %d", vertexCount, vertexCountLimit);
|
|
vertexCount = vertexCountLimit;
|
|
}
|
|
}
|
|
|
|
// At this point, rect and line primitives are still preserved as such. So, it's the best time to do software depth raster.
|
|
// We could piggyback on the viewport transform below, but it gets complicated since it's different per-backend. Which we really
|
|
// should clean up one day...
|
|
if (useDepthRaster_) {
|
|
DepthRasterPredecoded(prim, decoded_, numDecodedVerts_, dec_, vertexCount);
|
|
}
|
|
|
|
bool textureNeedsApply = false;
|
|
TextureApplyResult textureResult;
|
|
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
|
|
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
|
gstate_c.dstSquared = false;
|
|
textureResult = textureCache_->ApplyTexture(true);
|
|
textureNeedsApply = true;
|
|
} else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) {
|
|
// This catches the case of clearing a texture. (#10957)
|
|
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
|
}
|
|
|
|
u16 *inds = decIndex_;
|
|
SoftwareTransformResult result{};
|
|
SoftwareTransformParams params{};
|
|
params.everUsedEqualDepth = everUsedEqualDepth_;
|
|
params.decoded = decoded_;
|
|
params.transformed = transformed_;
|
|
params.transformedExpanded = transformedExpanded_;
|
|
params.allowClear = true; // Clear in OpenGL respects scissor rects, so we'll use it.
|
|
params.allowSeparateAlphaClear = true;
|
|
params.clipInfoFlags = clipInfoFlags_;
|
|
|
|
const SoftwareTransformAction action = RunSoftwareTransform(params, prim, dec_->VertexType(), dec_->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result);
|
|
if (textureNeedsApply) {
|
|
textureCache_->ApplySampler(textureResult, clipInfoFlags_ & ClipInfoFlags::FlatZ, result.pixelMapped);
|
|
if (gstate_c.dstSquared) {
|
|
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
|
}
|
|
}
|
|
|
|
// Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state.
|
|
ApplyDrawState(prim);
|
|
ApplyDrawStateLate(result.setStencil, result.stencilValue);
|
|
|
|
LinkedShader *linked = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, clipInfoFlags_, result.pixelMapped);
|
|
if (!linked) {
|
|
// Not much we can do here. Let's skip drawing.
|
|
goto bail;
|
|
}
|
|
|
|
if (action == SW_DRAW_INDEXED) {
|
|
vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, result.drawVertexCount * sizeof(TransformedVertex), 4, &vertexBuffer);
|
|
indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * result.drawIndexCount, 2, &indexBuffer);
|
|
render_->DrawIndexed(
|
|
softwareInputLayout_, vertexBuffer, vertexBufferOffset, indexBuffer, indexBufferOffset,
|
|
glprim[prim], result.drawIndexCount, GL_UNSIGNED_SHORT);
|
|
gpuStats.perFrame.numVertsDrawn += result.drawIndexCount;
|
|
} else if (action == SW_CLEAR) {
|
|
u32 clearColor = result.color;
|
|
float clearDepth = result.depth;
|
|
|
|
bool colorMask = gstate.isClearModeColorMask();
|
|
bool alphaMask = gstate.isClearModeAlphaMask();
|
|
bool depthMask = gstate.isClearModeDepthMask();
|
|
|
|
GLbitfield target = 0;
|
|
// Without this, we will clear RGB when clearing stencil, which breaks games.
|
|
uint8_t rgbaMask = (colorMask ? 7 : 0) | (alphaMask ? 8 : 0);
|
|
if (colorMask || alphaMask) target |= GL_COLOR_BUFFER_BIT;
|
|
if (alphaMask) target |= GL_STENCIL_BUFFER_BIT;
|
|
if (depthMask) target |= GL_DEPTH_BUFFER_BIT;
|
|
|
|
render_->Clear(clearColor, clearDepth, clearColor >> 24, target, rgbaMask, vpAndScissor_.scissorX, vpAndScissor_.scissorY, vpAndScissor_.scissorW, vpAndScissor_.scissorH);
|
|
|
|
if (gstate_c.Use(GPU_USE_CLEAR_RAM_HACK) && colorMask && (alphaMask || gstate_c.framebufFormat == GE_FORMAT_565)) {
|
|
int scissorX1 = gstate.getScissorX1();
|
|
int scissorY1 = gstate.getScissorY1();
|
|
int scissorX2 = gstate.getScissorX2() + 1;
|
|
int scissorY2 = gstate.getScissorY2() + 1;
|
|
framebufferManager_->ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, clearColor);
|
|
}
|
|
gstate_c.Dirty(DIRTY_BLEND_STATE); // Make sure the color mask gets re-applied.
|
|
}
|
|
}
|
|
|
|
bail:
|
|
ResetAfterDrawInline();
|
|
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
|
|
gpuCommon_->NotifyFlush();
|
|
}
|