mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Refactor] (P13 W6b): the frontend-arm code after each record-arm return goes, and the transform-feedback state keeps one role.
This commit is contained in:
@@ -247,17 +247,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, GL_ALWAYS);
|
||||
}
|
||||
return native.get();
|
||||
if (!g_rawDepthFetchSamplerState) {
|
||||
g_rawDepthFetchSamplerState = MakeShared<MG_State::GLState::SamplerObject>(0);
|
||||
g_rawDepthFetchSamplerState->SetMinFilter(SamplerFilterMode::Nearest);
|
||||
g_rawDepthFetchSamplerState->SetMagFilter(SamplerFilterMode::Nearest);
|
||||
g_rawDepthFetchSamplerState->SetMipmapMode(SamplerMipmapMode::None);
|
||||
g_rawDepthFetchSamplerState->SetCompareMode(SamplerCompareMode::None);
|
||||
g_rawDepthFetchSamplerState->SetSamplerCompareFunc(SamplerCompareFunc::Always);
|
||||
g_rawDepthFetchSamplerBackend = MakeShared<SamplerImpl::BackendSamplerObject>();
|
||||
}
|
||||
g_rawDepthFetchSamplerBackend->SyncToBackend(g_rawDepthFetchSamplerState);
|
||||
return g_rawDepthFetchSamplerBackend.get();
|
||||
}
|
||||
|
||||
// P5e (tx2), CONTRACT-P5E §5.3: the substitution's decision, over the fifteen sampler VALUES
|
||||
@@ -391,23 +380,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
MGLOG_E_ONCE("%s skipped: the verb carried no indirect buffer handle", label);
|
||||
return nullptr;
|
||||
auto drawBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (drawBuffer) {
|
||||
drawBuffer->SyncPersistentMappedRange();
|
||||
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
|
||||
if (commandOffset + requiredBytes > drawBuffer->GetSize()) {
|
||||
MGLOG_E_ONCE("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label);
|
||||
return nullptr;
|
||||
}
|
||||
return drawBuffer->MappedData() + commandOffset;
|
||||
}
|
||||
|
||||
if (!indirect) {
|
||||
MGLOG_E_ONCE("%s skipped: indirect pointer is null", label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<const Uint8*>(indirect);
|
||||
}
|
||||
|
||||
namespace DebugImpl {
|
||||
@@ -802,16 +774,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// The applier's ShaderBufferWritableMask survives as the server's record of WHICH
|
||||
// points a shader may write through, which is what P9's narrowing channel will name.
|
||||
return;
|
||||
const SizeT bindingPointCnt =
|
||||
MG_Pipe::gPipeInputs.GetTouchedBufferBindingPointCount(BufferTarget::ShaderStorage);
|
||||
for (SizeT i = 0; i < bindingPointCnt; ++i) {
|
||||
const auto& obj =
|
||||
MG_Pipe::gPipeInputs.GetBufferBindingPoint(BufferTarget::ShaderStorage, i).GetBoundObject();
|
||||
// P3a (D-D): announced on the reverse channel on the handle arm, poked into
|
||||
// the object on the legacy one. MarkBufferGpuWritten is the one place that
|
||||
// decides, so the three announcement sites stay one line each.
|
||||
MarkBufferGpuWritten(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncAtomicCounterBuffers(const Vector<Int>& glBindings, Int esslBindingTop) {
|
||||
@@ -1678,29 +1640,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
resource->syncedChangeSerial = BufferImpl::ResourceSerialForHandle(res);
|
||||
BufferImpl::BumpBufferMutationEpoch();
|
||||
continue;
|
||||
if (target.buffer->IsBackendPersistentMapped()) continue;
|
||||
const SizeT size = target.end - target.start;
|
||||
BufferImpl::BindBufferId(BufferImpl::TempBufferTarget, target.backendId);
|
||||
void* mapped = g_GLESFuncs.glMapBufferRange(BufferImpl::TempBufferTarget,
|
||||
static_cast<GLintptr>(target.start),
|
||||
static_cast<GLsizeiptr>(size), GL_MAP_READ_BIT);
|
||||
if (mapped == nullptr) {
|
||||
// Silent before: the capture landed in the ES buffer and the
|
||||
// application's next glMapBuffer read the untouched shadow, which
|
||||
// is indistinguishable from "the draw wrote nothing".
|
||||
MGLOG_E_ONCE("EndTransformFeedback: failed to map backend buffer %u [%zu, %zu) for "
|
||||
"capture readback (ES error %s); the captured data will NOT be visible to "
|
||||
"the application",
|
||||
target.backendId, target.start, target.end,
|
||||
MG_Util::ConvertGLEnumToString(TakeXfbDriverError()).c_str());
|
||||
continue;
|
||||
}
|
||||
target.buffer->WritebackFromBackend({mapped, size}, target.start);
|
||||
// WritebackFromBackend bumps the frontend change serial with no
|
||||
// backend op, leaving the buffer draw-dirty behind the epoch's
|
||||
// back; re-open the draw-clean memos.
|
||||
BufferImpl::BumpBufferMutationEpoch();
|
||||
g_GLESFuncs.glUnmapBuffer(BufferImpl::TempBufferTarget);
|
||||
}
|
||||
}
|
||||
targets.clear();
|
||||
@@ -1964,17 +1903,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
BufferImpl::BindBufferRangeCached(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(i),
|
||||
resource->id, static_cast<GLintptr>(range.Offset), static_cast<GLsizeiptr>(range.Size));
|
||||
continue;
|
||||
auto& point = MG_Pipe::gPipeInputs.GetBufferBindingPoint(BufferTarget::TransformFeedback,
|
||||
static_cast<Uint>(i));
|
||||
const auto& bufferObject = point.GetBoundObject();
|
||||
if (!bufferObject) continue;
|
||||
auto* backendResource = BufferImpl::EnsureBufferResource(bufferObject);
|
||||
if (!backendResource || backendResource->id == 0) continue;
|
||||
const Range1D range = point.GetRange();
|
||||
const SizeT start = std::min(range.start, bufferObject->GetSize());
|
||||
const SizeT end = std::min(range.end, bufferObject->GetSize());
|
||||
if (end <= start) continue;
|
||||
xfb.targets.push_back({bufferObject, backendResource->id, start, end, i});
|
||||
}
|
||||
|
||||
|
||||
@@ -2128,7 +2056,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
auto& xfb = g_xfbObjects[key];
|
||||
// Even each virtual context's name-0 object gets its own native object.
|
||||
if ((server || name != 0) && xfb.esId == 0) {
|
||||
if (xfb.esId == 0) {
|
||||
g_GLESFuncs.glGenTransformFeedbacks(1, &xfb.esId);
|
||||
}
|
||||
g_GLESFuncs.glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb.esId);
|
||||
@@ -2156,15 +2084,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (g_currentXfbName ==
|
||||
key
|
||||
) {
|
||||
if (server) {
|
||||
// The following BindStreamOutput names this context's actual
|
||||
// default lifetime. Never create a native object for key 0.
|
||||
g_currentXfbName = 0;
|
||||
roleState.NeedsBind = true;
|
||||
BufferImpl::InvalidateTransformFeedbackBindingShadows();
|
||||
return;
|
||||
}
|
||||
BindTransformFeedback(0);
|
||||
// The following BindStreamOutput names this context's actual
|
||||
// default lifetime. Never create a native object for key 0.
|
||||
g_currentXfbName = 0;
|
||||
roleState.NeedsBind = true;
|
||||
BufferImpl::InvalidateTransformFeedbackBindingShadows();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2172,7 +2097,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// driver objects and the frontend objects they pinned all belonged to it.
|
||||
void OnBackendContextDestroyed() {
|
||||
for (auto& state : g_roleXfbState) state = {};
|
||||
g_activeXfbRole = -1;
|
||||
g_currentXfbState = nullptr;
|
||||
g_xfbObjects.clear();
|
||||
g_currentXfbName = 0;
|
||||
@@ -2354,50 +2278,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// server to stage - and reading gPipeInputs there is the role violation this family's
|
||||
// other monolith glue is guarded against.
|
||||
return true;
|
||||
const auto& currentVAO = MG_Pipe::gPipeInputs.GetBoundVertexArray();
|
||||
if (!currentVAO) return true;
|
||||
|
||||
// A draw whose attributes all come from buffers has nothing to snapshot, and asking
|
||||
// the plan would read the index stream to answer a question nobody asked.
|
||||
Bool clientVertexRate = false;
|
||||
if (!VaoHasEnabledClientArray(*currentVAO, &clientVertexRate)) return true;
|
||||
|
||||
auto* twin = ResolveVaoTwin(currentVAO);
|
||||
if (twin == nullptr) return false;
|
||||
|
||||
const SharedPtr<MG_State::GLState::BufferObject> elementBuffer =
|
||||
currentVAO->GetIndexBufferBindingSlot().GetBoundObject();
|
||||
ClientSnapshotSources sources{&elementBuffer};
|
||||
|
||||
MG_Pipe::MGPDrawRange range{elementStart, static_cast<Uint32>(count), baseVertex};
|
||||
MG_Pipe::MGPipeClientDrawInputs inputs{};
|
||||
inputs.IndexSize = indexSize;
|
||||
inputs.Ranges = ⦥
|
||||
inputs.RangeCount = 1;
|
||||
inputs.InstanceCount = static_cast<Uint32>(instanceCount);
|
||||
inputs.BaseInstance = baseInstance;
|
||||
inputs.ClientIndices = clientIndices;
|
||||
inputs.ClientIndexBytes = clientIndexBytes;
|
||||
inputs.WantVertices = clientVertexRate;
|
||||
// The driver does its own restart handling; the plan has to know only that the restart
|
||||
// value is not a vertex, or the snapshot would stage an element no primitive fetches.
|
||||
inputs.PrimitiveRestart = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) ||
|
||||
MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex);
|
||||
inputs.RestartIndex = MG_Pipe::gPipeInputs.IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex)
|
||||
? (indexSize == 1 ? 0xffu : indexSize == 2 ? 0xffffu : 0xffffffffu)
|
||||
: MG_Pipe::gPipeInputs.GetPrimitiveRestartIndex();
|
||||
|
||||
MG_Pipe::MGPipeClientFetchPlan plan;
|
||||
if (!plan.Build(inputs, &ReadClientSnapshotBytes, &sources)) return false;
|
||||
// The element SET is GL's: a divisor'd array's instance elements start at the raw
|
||||
// baseInstance. The POINTER shift is not - it is this backend's emulation of what the
|
||||
// driver does natively when it can (EmulatedFetchBaseInstance is the same answer the
|
||||
// attribute walk uses), and applying both would shift the fetch twice.
|
||||
// EmulatedFetchBaseInstance, which is declared with the draw entry points far below:
|
||||
// a native-baseInstance driver applies the shift itself, so the pointer carries none.
|
||||
const Uint32 fetchBaseInstance =
|
||||
g_GLESCapabilities.SupportsBaseInstance ? 0u : static_cast<Uint32>(baseInstance);
|
||||
return twin->SyncClientSideAttributesForDraw(currentVAO, plan, fetchBaseInstance);
|
||||
}
|
||||
|
||||
// The indirect executors' form: the command's own words ARE the draw's fetch, and a
|
||||
@@ -2408,35 +2288,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const Uint8* commandBytes, SizeT commandOffset, GLsizei stride, GLsizei index, Uint8 indexSize,
|
||||
const SharedPtr<MG_State::GLState::BufferObject>& commandBuffer) {
|
||||
return true;
|
||||
// P13 W4a (ID-P8-14a): ASK FIRST WHETHER THERE IS ANYTHING TO SNAPSHOT. The command
|
||||
// words below are read only to size a client-memory fetch; a VAO whose attributes all
|
||||
// come from buffers has none, and reading them anyway cost a whole-buffer readback of a
|
||||
// GPU-written command buffer per command, per viewport pass (the hang 62bfe461 left
|
||||
// behind on a phone).
|
||||
{
|
||||
const auto& currentVAO = MG_Pipe::gPipeInputs.GetBoundVertexArray();
|
||||
if (!currentVAO || !VaoHasEnabledClientArray(*currentVAO)) return true;
|
||||
}
|
||||
const Uint8* source = commandBytes;
|
||||
if (commandBuffer) {
|
||||
commandBuffer->SyncGpuWrites();
|
||||
const Uint8* mapped = commandBuffer->MappedData();
|
||||
if (mapped == nullptr) return false;
|
||||
source = mapped + commandOffset;
|
||||
}
|
||||
const SizeT at = static_cast<SizeT>(index) * static_cast<SizeT>(stride);
|
||||
if (indexSize == 0) {
|
||||
DrawArraysIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, source + at, sizeof(cmd));
|
||||
return SyncClientSideVertexArraysForFetch(0, cmd.first, static_cast<GLsizei>(cmd.count),
|
||||
static_cast<GLsizei>(cmd.instanceCount), 0, cmd.baseInstance,
|
||||
nullptr, 0);
|
||||
}
|
||||
DrawElementsIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, source + at, sizeof(cmd));
|
||||
return SyncClientSideVertexArraysForFetch(indexSize, cmd.firstIndex, static_cast<GLsizei>(cmd.count),
|
||||
static_cast<GLsizei>(cmd.instanceCount), cmd.baseVertex,
|
||||
static_cast<Uint32>(cmd.baseInstance), nullptr, 0);
|
||||
}
|
||||
|
||||
// The indexed entry points' form. `indices` is a byte offset into the bound element buffer
|
||||
@@ -2449,31 +2300,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// MONOLITH ONLY - see SyncClientSideVertexArraysForFetch; this wrapper's own read of
|
||||
// the bound VAO is the frontend read that must not happen on an apply thread.
|
||||
return true;
|
||||
const auto& currentVAO = MG_Pipe::gPipeInputs.GetBoundVertexArray();
|
||||
if (!currentVAO) return true;
|
||||
const Uint8 indexSize = static_cast<Uint8>(MG_Util::GetGLTypeSize(type));
|
||||
if (indexSize == 0) return true;
|
||||
|
||||
const Bool clientIndices = !currentVAO->GetIndexBufferBindingSlot().GetBoundObject();
|
||||
Uint32 elementStart = 0;
|
||||
Uint64 clientIndexBytes = 0;
|
||||
if (clientIndices) {
|
||||
if (indices == nullptr) return false;
|
||||
clientIndexBytes = static_cast<Uint64>(count) * indexSize;
|
||||
} else {
|
||||
const Uint64 byteOffset = reinterpret_cast<Uint64>(indices);
|
||||
if (byteOffset % indexSize != 0 ||
|
||||
byteOffset / indexSize > std::numeric_limits<Uint32>::max()) {
|
||||
MGLOG_E_ONCE("An indexed draw over a client-memory vertex array names byte offset %llu, "
|
||||
"which is not a whole number of %u-byte indices; the draw is skipped",
|
||||
static_cast<unsigned long long>(byteOffset), static_cast<Uint>(indexSize));
|
||||
return false;
|
||||
}
|
||||
elementStart = static_cast<Uint32>(byteOffset / indexSize);
|
||||
}
|
||||
return SyncClientSideVertexArraysForFetch(indexSize, elementStart, count, instanceCount, baseVertex,
|
||||
baseInstance, clientIndices ? indices : nullptr,
|
||||
clientIndexBytes);
|
||||
}
|
||||
|
||||
static void SyncClientSideVertexArraysForDrawArrays(GLint first, GLsizei count) {
|
||||
@@ -4093,27 +3919,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
return;
|
||||
if (g_writableImageBufferUnitCount == 0) return;
|
||||
for (Uint unit = 0; unit < g_writableImageBufferUnits.size(); ++unit) {
|
||||
if (!g_writableImageBufferUnits[unit]) continue;
|
||||
const auto& imageBinding = MG_Pipe::gPipeInputs.GetImageTextureBinding(static_cast<Int>(unit));
|
||||
if (!IsWritableImageBufferTexture(imageBinding)) {
|
||||
TrackWritableImageBufferUnit(unit, false);
|
||||
continue;
|
||||
}
|
||||
auto* textureBuffer =
|
||||
static_cast<MG_State::GLState::TextureObjectBuffer*>(imageBinding.Texture.get());
|
||||
const auto& bufferObject = textureBuffer->GetBufferBindingSlot().GetBoundObject();
|
||||
BufferImpl::MarkBufferGpuWritten(bufferObject);
|
||||
// P13 W4a: image units stay on this arm until W4c, but monolith's buffer readers
|
||||
// are already the record arm's (SplitHostBytesForCpuRead), and they ask the TWIN.
|
||||
if (bufferObject) {
|
||||
if (auto* twin = BufferImpl::FindBufferResourceForHandle(
|
||||
BufferImpl::HandleOfBuffer(bufferObject.get()))) {
|
||||
twin->serverGpuWritten = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyncImageTextureBindings() {
|
||||
@@ -4196,10 +4001,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
SyncImageTextureBinding(view);
|
||||
}
|
||||
return;
|
||||
for (Uint32 unit = 0; unit < end; ++unit) {
|
||||
SyncImageTextureBinding(unit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The wide pre-handle sweep reads the frontend binding of every unit, which an
|
||||
@@ -4208,9 +4009,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// ever arrived no unit has ever been given an image, and re-binding 0 on units that
|
||||
// never held one is the no-op the comment above already proved.
|
||||
return;
|
||||
for (Uint unit = 0; unit < unitCount; ++unit) {
|
||||
SyncImageTextureBinding(unit);
|
||||
}
|
||||
}
|
||||
|
||||
// What the draw path last swept the image units against. A draw never swept them at all:
|
||||
@@ -4288,16 +4086,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_imageSweepBackendContextGeneration = g_backendContextGeneration;
|
||||
g_imageSweepValid = true;
|
||||
return;
|
||||
if (g_imageSweepValid && g_imageSweepContextId == keys.contextId &&
|
||||
g_imageSweepSamplingGeneration == keys.samplingGeneration &&
|
||||
g_imageSweepBackendContextGeneration == g_backendContextGeneration) {
|
||||
return;
|
||||
}
|
||||
SyncImageTextureBindings();
|
||||
g_imageSweepContextId = keys.contextId;
|
||||
g_imageSweepSamplingGeneration = keys.samplingGeneration;
|
||||
g_imageSweepBackendContextGeneration = g_backendContextGeneration;
|
||||
g_imageSweepValid = true;
|
||||
}
|
||||
} // namespace TextureImpl
|
||||
|
||||
@@ -4422,7 +4210,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// applier's OWN bound handle, so it is this binding's by construction (ID-19).
|
||||
// Monolith keeps the corroboration verbatim.
|
||||
return true;
|
||||
return record.Fbo == g_backendFramebufferObjects.HandleOf(bound.get());
|
||||
}
|
||||
|
||||
static Bool SyncedFramebufferSerialIsCurrent(FramebufferTarget target, Uint64 serial) {
|
||||
@@ -8987,48 +8774,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
nullptr, static_cast<GLsizei>(actualDrawCount), stride,
|
||||
"MultiDrawElementsIndirectCount");
|
||||
return;
|
||||
|
||||
auto drawBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
auto parameterBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
|
||||
if (!drawBuffer) {
|
||||
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
if (!parameterBuffer) {
|
||||
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
|
||||
drawBuffer->SyncPersistentMappedRange();
|
||||
parameterBuffer->SyncPersistentMappedRange();
|
||||
|
||||
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
|
||||
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(maxdrawcount - 1) +
|
||||
sizeof(DrawElementsIndirectCommand);
|
||||
if (commandBytes > drawBuffer->GetSize()) {
|
||||
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
if (drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
|
||||
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
// Both counts are read from the CPU shadow, which a buffer with no shadow does not
|
||||
// have - MappedData() is null there and the reads below would be a null dereference,
|
||||
// not a wrong picture. The DirectVulkan twin declines the same way.
|
||||
if (parameterBuffer->MappedData() == nullptr || drawBuffer->MappedData() == nullptr) {
|
||||
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: CPU fallback cannot read the parameter or "
|
||||
"draw-indirect buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
Uint32 actualDrawCount = 0;
|
||||
std::memcpy(&actualDrawCount, parameterBuffer->MappedData() + drawcount, sizeof(actualDrawCount));
|
||||
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||
ExecuteIndexedIndirectCommands(mode, type, indexSize, drawBuffer->MappedData() + commandOffset, commandOffset,
|
||||
drawBuffer, static_cast<GLsizei>(actualDrawCount), stride,
|
||||
"MultiDrawElementsIndirectCount");
|
||||
}
|
||||
|
||||
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {
|
||||
@@ -9142,45 +8887,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static_cast<GLsizei>(actualDrawCount), stride,
|
||||
"MultiDrawArraysIndirectCount");
|
||||
return;
|
||||
|
||||
auto drawBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
auto parameterBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
|
||||
if (!drawBuffer) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
if (!parameterBuffer) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
|
||||
drawBuffer->SyncPersistentMappedRange();
|
||||
parameterBuffer->SyncPersistentMappedRange();
|
||||
|
||||
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
|
||||
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(maxdrawcount - 1) +
|
||||
sizeof(DrawArraysIndirectCommand);
|
||||
if (commandBytes > drawBuffer->GetSize()) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
if (drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
// See the indexed twin: no CPU shadow means no count to read, not a wrong one.
|
||||
if (parameterBuffer->MappedData() == nullptr || drawBuffer->MappedData() == nullptr) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read the parameter or "
|
||||
"draw-indirect buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
Uint32 actualDrawCount = 0;
|
||||
std::memcpy(&actualDrawCount, parameterBuffer->MappedData() + drawcount, sizeof(actualDrawCount));
|
||||
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||
ExecuteArraysIndirectCommands(mode, drawBuffer->MappedData() + commandOffset, commandOffset, drawBuffer,
|
||||
static_cast<GLsizei>(actualDrawCount), stride, "MultiDrawArraysIndirectCount");
|
||||
}
|
||||
|
||||
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||
@@ -10760,26 +10466,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
backendObj->Bind(TextureImpl::ConvertTextureTargetToBackendGLEnum(textureTarget), unit);
|
||||
return true;
|
||||
|
||||
auto& textureUnit = MG_Pipe::gPipeInputs.GetTextureUnitObject(unit);
|
||||
const auto& bindingSlot = textureUnit.GetBindingSlot(textureTarget);
|
||||
{
|
||||
const auto& textureObject = bindingSlot.GetBoundObject();
|
||||
if (!textureObject) {
|
||||
MGLOG_D("%s: Texture target %s does not have texture bound.", __func__,
|
||||
MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
|
||||
}
|
||||
|
||||
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.Find(textureObject.get());
|
||||
auto& backendObj = backendTextureSlot
|
||||
? *backendTextureSlot
|
||||
: TextureImpl::g_backendTextureObjects.GetOrCreate(textureObject);
|
||||
if (!backendObj) {
|
||||
backendObj = MakeShared<TextureImpl::BackendTextureObject>();
|
||||
}
|
||||
backendObj->Bind(TextureImpl::ConvertTextureTargetToBackendGLEnum(textureTarget), unit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- Scoped driver-state guards for the readback/copy/blit emulation paths --------------------
|
||||
@@ -12465,52 +12151,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
GenerateMipmapByRecord(target, mipRes, *record, backendTexture, driverWrittenBefore);
|
||||
return;
|
||||
auto unitIndex = MG_Pipe::gPipeInputs.GetActiveTextureUnit();
|
||||
auto& unit = MG_Pipe::gPipeInputs.GetTextureUnitObject(unitIndex);
|
||||
auto& slot = unit.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target));
|
||||
auto& texture = slot.GetBoundObject();
|
||||
MOBILEGL_ASSERT(texture != nullptr, "GenerateMipmap requires a bound texture.");
|
||||
if (texture->GetFormat() == TextureInternalFormat::R11FG11FB10F || IsDepthOnlyFormat(texture->GetFormat()) ||
|
||||
texture->GetFormat() == TextureInternalFormat::RGB16F ||
|
||||
texture->GetFormat() == TextureInternalFormat::RGB32F) {
|
||||
EnsureGenerateMipmapStorageAllocated(texture);
|
||||
}
|
||||
// Filtered on the CPU before the backend sync, so the dirty levels ride down with it.
|
||||
if (GenerateThreeChannelFloatMipmapOnCpu(texture)) {
|
||||
TextureImpl::SyncTextureObjectToBackend(texture);
|
||||
return;
|
||||
}
|
||||
// Every path below writes the levels above the base on the driver only.
|
||||
TextureImpl::NoteDriverSideTextureWrite(texture);
|
||||
auto& backendTexture = TextureImpl::SyncTextureObjectToBackend(texture);
|
||||
|
||||
// Asked for before the format arms below, which are 2D-only emulations of the same
|
||||
// generation and reach a view just as well.
|
||||
if (GenerateMipmapThroughViewWindowForTexture(texture)) return;
|
||||
if (IsDepthOnlyFormat(texture->GetFormat())) {
|
||||
GenerateDepthTexture2DMipmap(texture, backendTexture);
|
||||
return;
|
||||
}
|
||||
if (texture->GetFormat() == TextureInternalFormat::R11FG11FB10F &&
|
||||
texture->GetTarget() == TextureTarget::Texture2D) {
|
||||
GenerateColorTexture2DMipmap(texture, backendTexture);
|
||||
return;
|
||||
}
|
||||
|
||||
const GLenum backendTarget =
|
||||
TextureImpl::ConvertTextureTargetToBackendGLEnum(MG_Util::ConvertGLEnumToTextureTarget(target));
|
||||
backendTexture->Bind(backendTarget, unitIndex);
|
||||
// ANGLE/Mesa may validate the currently bound FBO while generating mipmaps.
|
||||
// Also detach the source texture from synced FBO objects for ANGLE's validation.
|
||||
ScopedDetachedTextureFramebufferAttachments detachedAttachments(texture);
|
||||
// Bind a complete internal FBO that does not reference the source texture.
|
||||
ScopedCompleteFramebufferBinding completeFramebuffer;
|
||||
// ErrorLopper is compiled out at the default log level; RecordGLError below
|
||||
// forwards the next queued error to the APP, so stale flags from earlier
|
||||
// best-effort calls must be drained by the always-live helper.
|
||||
ClearGLErrors();
|
||||
g_GLESFuncs.glGenerateMipmap(backendTarget);
|
||||
RecordGLError("glGenerateMipmap", backendTarget, texture->GetFormat());
|
||||
}
|
||||
|
||||
const GLubyte* GetString(GLenum name) {
|
||||
@@ -12719,61 +12359,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// integration-gpu lanes under MOBILEGL_TRANSPORT=monolith, where this mirror is on an
|
||||
// ordinary correct path and must still run.
|
||||
return;
|
||||
// P4a (D-M) named this site `copy-image-shadow-mirror` with MGPipeUnmigratedEmulation.
|
||||
// P8-SE removed the call: the return above means no transport reaches it, and on a
|
||||
// monolith arm the call was a no-op (PipeCatalogueTest's list and the lint
|
||||
// scripts/ci/unmigrated_emulation_sites.py follow).
|
||||
|
||||
const auto srcUploadTarget = srcEndpoint.Texture->GetUploadTargets()[0];
|
||||
const auto dstUploadTarget = dstEndpoint.Texture->GetUploadTargets()[0];
|
||||
const IntVec3 srcSize = srcMipmap->GetMipmapTexelSize(srcUploadTarget, static_cast<Uint>(srcLevel));
|
||||
const IntVec3 dstSize = dstMipmap->GetMipmapTexelSize(dstUploadTarget, static_cast<Uint>(dstLevel));
|
||||
const SizeT srcSlices = static_cast<SizeT>(std::max(srcSize.z(), 1));
|
||||
const SizeT dstSlices = static_cast<SizeT>(std::max(dstSize.z(), 1));
|
||||
if (srcSize.x() <= 0 || srcSize.y() <= 0 || dstSize.x() <= 0 || dstSize.y() <= 0) return;
|
||||
const SizeT srcTexels = static_cast<SizeT>(srcSize.x()) * static_cast<SizeT>(srcSize.y()) * srcSlices;
|
||||
const SizeT dstTexels = static_cast<SizeT>(dstSize.x()) * static_cast<SizeT>(dstSize.y()) * dstSlices;
|
||||
const SizeT srcBytes = srcMipmap->GetMipmapByteSize(srcUploadTarget, static_cast<Uint>(srcLevel));
|
||||
const SizeT dstBytes = dstMipmap->GetMipmapByteSize(dstUploadTarget, static_cast<Uint>(dstLevel));
|
||||
// A shadow that is not exactly texels x texelSize bytes is one this cannot index (a
|
||||
// compressed blob, or a level whose allocation disagrees with its recorded extent).
|
||||
const SizeT texelBytes = srcTexels == 0 ? 0 : srcBytes / srcTexels;
|
||||
if (texelBytes == 0 || srcBytes != srcTexels * texelBytes || dstTexels == 0 ||
|
||||
dstBytes != dstTexels * texelBytes) {
|
||||
return;
|
||||
}
|
||||
if (static_cast<SizeT>(srcX) + width > static_cast<SizeT>(srcSize.x()) ||
|
||||
static_cast<SizeT>(srcY) + height > static_cast<SizeT>(srcSize.y()) ||
|
||||
static_cast<SizeT>(srcZ) + depth > srcSlices ||
|
||||
static_cast<SizeT>(dstX) + width > static_cast<SizeT>(dstSize.x()) ||
|
||||
static_cast<SizeT>(dstY) + height > static_cast<SizeT>(dstSize.y()) ||
|
||||
static_cast<SizeT>(dstZ) + depth > dstSlices) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* srcBase = static_cast<const Uint8*>(
|
||||
srcMipmap->MapMipmapData(srcUploadTarget, static_cast<Uint>(srcLevel)));
|
||||
auto* dstBase = static_cast<Uint8*>(dstMipmap->MapMipmapData(dstUploadTarget, static_cast<Uint>(dstLevel)));
|
||||
if (!srcBase || !dstBase) return;
|
||||
|
||||
const SizeT rowBytes = static_cast<SizeT>(width) * texelBytes;
|
||||
for (GLsizei slice = 0; slice < depth; ++slice) {
|
||||
for (GLsizei row = 0; row < height; ++row) {
|
||||
const SizeT srcOffset = ((static_cast<SizeT>(srcZ + slice) * static_cast<SizeT>(srcSize.y()) +
|
||||
static_cast<SizeT>(srcY + row)) *
|
||||
static_cast<SizeT>(srcSize.x()) +
|
||||
static_cast<SizeT>(srcX)) *
|
||||
texelBytes;
|
||||
const SizeT dstOffset = ((static_cast<SizeT>(dstZ + slice) * static_cast<SizeT>(dstSize.y()) +
|
||||
static_cast<SizeT>(dstY + row)) *
|
||||
static_cast<SizeT>(dstSize.x()) +
|
||||
static_cast<SizeT>(dstX)) *
|
||||
texelBytes;
|
||||
Memcpy(dstBase + dstOffset, srcBase + srcOffset, rowBytes);
|
||||
}
|
||||
}
|
||||
MGLOG_D("CopyImageSubData: mirrored %dx%dx%d texels into the destination's CPU shadow", width, height,
|
||||
depth);
|
||||
}
|
||||
|
||||
void CopyImageSubData(const CopyImageEndpoint& srcEndpoint,
|
||||
@@ -12924,8 +12509,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// barriered row.
|
||||
TextureImpl::NoteImageUnitBoundWithoutReadingTheFrontend(unit, texture != 0);
|
||||
return;
|
||||
(void)texture;
|
||||
TextureImpl::SyncImageTextureBinding(unit);
|
||||
}
|
||||
|
||||
// Only the pnames MG_Impl/GLImpl/Getter/GL_Getter.cpp has no case for reach here. Every
|
||||
@@ -12972,25 +12555,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
storageBlockName, storageBlockBinding);
|
||||
}
|
||||
return;
|
||||
if (!MG_Pipe::gPipeInputs.ValidateProgramName(program)) return;
|
||||
auto& programObject = MG_Pipe::gPipeInputs.GetProgramObject(program);
|
||||
if (!programObject) return;
|
||||
|
||||
// P5c (G6, CONTRACT-P5C §5.4): frontend-keyed twin resolution, named debt inside the
|
||||
// scope - P3b/P4b rekeys the registry onto handles.
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
auto* backendProgramSlot = PrgramImpl::g_backendProgramObjects.Find(programObject.get());
|
||||
if (!backendProgramSlot || !*backendProgramSlot) return;
|
||||
auto& backendObj = *backendProgramSlot;
|
||||
// Not merely "a program id exists": a backend object whose synced link version has
|
||||
// fallen behind is about to be rebuilt anyway, and its current driver interface is
|
||||
// the PREVIOUS link's - applying to it could land the binding on an unrelated block.
|
||||
if (!backendObj->GetBackendProgramId() ||
|
||||
backendObj->GetSyncedLinkVersion() != programObject->GetLinkVersion()) {
|
||||
return; // SyncToBackend's reseed will carry it
|
||||
}
|
||||
PrgramImpl::ApplyShaderStorageBlockBinding(backendObj->GetBackendProgramId(), storageBlockName,
|
||||
storageBlockBinding);
|
||||
}
|
||||
|
||||
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
|
||||
|
||||
@@ -3956,8 +3956,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// 0xDD audit could not reach a draw. R-2 / table 3: honest inproc is inproc that
|
||||
// does not read the client object's memory. Under monolith nothing changes.
|
||||
return resource->hostBytes;
|
||||
if (bufferObject) return bufferObject->MappedData();
|
||||
return resource->hostBytes;
|
||||
};
|
||||
|
||||
if (resource->contextGeneration != g_bufferContextGeneration) {
|
||||
@@ -11216,11 +11214,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Bool IsAlphaWidenedFallbackReadAttachment() {
|
||||
const auto* record = MG_Pipe::MGPipeApplier().ReadFramebuffer();
|
||||
return record && IsAlphaWidenedColorSurface(record->ReadSurface);
|
||||
const auto* attachmentObject = GetReadColorAttachment();
|
||||
if (attachmentObject == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return IsAlphaWidenedColorAttachment(*attachmentObject);
|
||||
}
|
||||
|
||||
Bool IsFixedPointFallbackReadAttachment() {
|
||||
@@ -12871,7 +12864,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const auto& st = MG_Pipe::MGPipeApplier();
|
||||
if (static_cast<SizeT>(unit) >= st.BoundShaderImages.size()) return 0;
|
||||
return static_cast<Uint>(st.BoundShaderImages[static_cast<SizeT>(unit)].InternalFormat);
|
||||
return static_cast<Uint>(MG_Pipe::gPipeInputs.GetImageTextureBinding(unit).Format);
|
||||
}
|
||||
|
||||
// Combines one (unit, format) pair into a running digest. Commutative, so the order
|
||||
|
||||
@@ -296,12 +296,6 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
|
||||
RefuseMissingIndirectBufferRecord("BoundDrawIndirectBufferId", res);
|
||||
}
|
||||
return resource->id;
|
||||
// MONOLITH GLUE from here down, token for token what this function did before.
|
||||
const auto& indirect =
|
||||
MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (!indirect) return 0;
|
||||
const auto* resource = BufferImpl::EnsureBufferResource(indirect);
|
||||
return resource ? resource->id : 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -395,105 +395,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {
|
||||
DrawWireIndirect(mode, type, indirect, drawcount, stride, true);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawElementsIndirect called with null GL context");
|
||||
pVulkanRenderer->MultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {
|
||||
DrawWireIndirect(mode, 0, indirect, drawcount, stride, false);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArraysIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawArraysIndirect called with null GL context");
|
||||
|
||||
if (drawcount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// With a bound GL_DRAW_INDIRECT_BUFFER the command parameters may be GPU-written
|
||||
// (e.g. by a compute shader), so consume them natively on the GPU.
|
||||
auto drawBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (drawBuffer) {
|
||||
pVulkanRenderer->MultiDrawArraysIndirect(mode, indirect, drawcount, stride);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stride == 0) {
|
||||
stride = sizeof(DrawArraysIndirectCommand);
|
||||
}
|
||||
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu",
|
||||
stride, sizeof(DrawArraysIndirectCommand));
|
||||
return;
|
||||
}
|
||||
|
||||
// No indirect buffer bound: the pointer refers to client memory.
|
||||
const auto* commandBytes = ResolveIndirectCommandBytes(
|
||||
indirect,
|
||||
static_cast<SizeT>(stride) * static_cast<SizeT>(drawcount - 1) + sizeof(DrawArraysIndirectCommand),
|
||||
"MultiDrawArraysIndirect");
|
||||
if (!commandBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||
DrawArraysIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, commandBytes + static_cast<SizeT>(i) * stride, sizeof(cmd));
|
||||
if (cmd.count == 0 || cmd.instanceCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DrawCmd payload{};
|
||||
payload.mode = mode;
|
||||
payload.params.vertexCount = cmd.count;
|
||||
payload.params.instanceCount = cmd.instanceCount;
|
||||
payload.params.firstVertex = cmd.first;
|
||||
payload.params.firstInstance = cmd.baseInstance;
|
||||
pVulkanRenderer->DrawArrays(payload);
|
||||
}
|
||||
}
|
||||
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride) {
|
||||
DrawWireIndirect(mode, type, indirect, maxdrawcount, stride, true, true, drawcount);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsIndirectCount called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawElementsIndirectCount called with null GL context");
|
||||
pVulkanRenderer->MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride);
|
||||
}
|
||||
void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride) {
|
||||
DrawWireIndirect(mode, 0, indirect, maxdrawcount, stride, false, true, drawcount);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArraysIndirectCount called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::MultiDrawArraysIndirectCount called with null GL context");
|
||||
|
||||
if (maxdrawcount <= 0) {
|
||||
return;
|
||||
}
|
||||
if (stride == 0) {
|
||||
stride = sizeof(DrawArraysIndirectCommand);
|
||||
}
|
||||
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: stride %d is smaller than command size %zu",
|
||||
stride, sizeof(DrawArraysIndirectCommand));
|
||||
return;
|
||||
}
|
||||
|
||||
auto parameterBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
|
||||
if (!parameterBuffer || drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
parameterBuffer->SyncPersistentMappedRange();
|
||||
if (parameterBuffer->MappedData() == nullptr) {
|
||||
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read parameter buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
Uint32 actualDrawCount = 0;
|
||||
std::memcpy(&actualDrawCount, parameterBuffer->MappedData() + drawcount, sizeof(actualDrawCount));
|
||||
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||
MultiDrawArraysIndirect(mode, indirect, static_cast<GLsizei>(actualDrawCount), stride);
|
||||
}
|
||||
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||
const void* indices, GLint basevertex) {
|
||||
@@ -537,47 +452,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) {
|
||||
DrawWireIndirect(mode, type, indirect, 1, 0, true);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawElementsIndirect called with null GL context");
|
||||
|
||||
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
|
||||
if (indexSize == 0) {
|
||||
MGLOG_E_ONCE("DrawElementsIndirect skipped: unsupported index type 0x%x", type);
|
||||
return;
|
||||
}
|
||||
|
||||
// With a bound GL_DRAW_INDIRECT_BUFFER the command parameters may be GPU-written
|
||||
// (e.g. by a compute shader), so consume them natively on the GPU.
|
||||
auto drawBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (drawBuffer) {
|
||||
pVulkanRenderer->MultiDrawElementsIndirect(mode, type, indirect, 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// No indirect buffer bound: the pointer refers to client memory.
|
||||
const auto* commandBytes =
|
||||
ResolveIndirectCommandBytes(indirect, sizeof(DrawElementsIndirectCommand), "DrawElementsIndirect");
|
||||
if (!commandBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
DrawElementsIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, commandBytes, sizeof(cmd));
|
||||
if (cmd.count == 0 || cmd.instanceCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DrawIndexedCmd payload{};
|
||||
payload.mode = mode;
|
||||
payload.indexBufferView.indexType = type;
|
||||
payload.indexBufferView.indexByteOffset = static_cast<SizeT>(cmd.firstIndex) * indexSize;
|
||||
payload.indexBufferView.indexByteSize = static_cast<SizeT>(cmd.count) * indexSize;
|
||||
payload.params.indexCount = cmd.count;
|
||||
payload.params.instanceCount = cmd.instanceCount;
|
||||
payload.params.firstIndex = 0;
|
||||
payload.params.vertexOffset = cmd.baseVertex;
|
||||
payload.params.firstInstance = static_cast<Int32>(cmd.baseInstance);
|
||||
pVulkanRenderer->DrawElements(payload);
|
||||
}
|
||||
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
|
||||
GLuint baseinstance) {
|
||||
@@ -598,37 +472,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void DrawArraysIndirect(GLenum mode, const void* indirect) {
|
||||
DrawWireIndirect(mode, 0, indirect, 1, 0, false);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArraysIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DrawArraysIndirect called with null GL context");
|
||||
|
||||
// With a bound GL_DRAW_INDIRECT_BUFFER the command parameters may be GPU-written
|
||||
// (e.g. by a compute shader), so consume them natively on the GPU.
|
||||
auto drawBuffer = MG_Pipe::gPipeInputs.GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (drawBuffer) {
|
||||
pVulkanRenderer->MultiDrawArraysIndirect(mode, indirect, 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// No indirect buffer bound: the pointer refers to client memory.
|
||||
const auto* commandBytes =
|
||||
ResolveIndirectCommandBytes(indirect, sizeof(DrawArraysIndirectCommand), "DrawArraysIndirect");
|
||||
if (!commandBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
DrawArraysIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, commandBytes, sizeof(cmd));
|
||||
if (cmd.count == 0 || cmd.instanceCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DrawCmd payload{};
|
||||
payload.mode = mode;
|
||||
payload.params.vertexCount = cmd.count;
|
||||
payload.params.instanceCount = cmd.instanceCount;
|
||||
payload.params.firstVertex = cmd.first;
|
||||
payload.params.firstInstance = cmd.baseInstance;
|
||||
pVulkanRenderer->DrawArrays(payload);
|
||||
}
|
||||
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
||||
GLsizei height, GLint border) {
|
||||
@@ -672,9 +515,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// that was here (ReadWireBuffer, a whole-GPU wait once a shader had written the store) is gone.
|
||||
pVulkanRenderer->DispatchComputeIndirect(indirect);
|
||||
return;
|
||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DispatchComputeIndirect called with null VulkanRenderer");
|
||||
MOBILEGL_ASSERT(MG_Pipe::gPipeInputs.IsLive(), "DirectVulkan::DispatchComputeIndirect called with null GL context");
|
||||
pVulkanRenderer->DispatchComputeIndirect(indirect);
|
||||
}
|
||||
|
||||
void MemoryBarrier(GLbitfield barriers) {
|
||||
@@ -748,31 +588,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
else found->Binding = static_cast<Int32>(storageBlockBinding);
|
||||
++record.BindingsSerial;
|
||||
return;
|
||||
auto* programObject = TryGetDirectVulkanProgram(program);
|
||||
if (!programObject || storageBlockName == nullptr) return;
|
||||
const Int maxBindings =
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// P5c (hd, CONTRACT-P5C §3.7): with an active transport the dynamic parameters are
|
||||
// the SERVER's own backend's - the client caps mirror (pActiveBackendObject) is
|
||||
// client memory the apply thread may not name (rule E). Monolith reads the mirror
|
||||
// as it always did.
|
||||
MG_Config::Transport != MG_Config::TransportMode::Monolith
|
||||
? (MG_Remote::Server::ServerLoopInstance().Backend() != nullptr
|
||||
? static_cast<Int>(MG_Remote::Server::ServerLoopInstance().Backend()
|
||||
->GetDynamicParameters()
|
||||
.MaxShaderStorageBufferBindings)
|
||||
: 0)
|
||||
:
|
||||
#endif
|
||||
pActiveBackendObject
|
||||
? pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings
|
||||
: 0;
|
||||
if (storageBlockBinding >= static_cast<GLuint>(maxBindings)) {
|
||||
MG_Pipe::gPipeInputs.RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("DirectVulkan", __func__, "Shader storage binding is out of range."));
|
||||
return;
|
||||
}
|
||||
// P7 wave 2 package C, OQ-8: NOTHING LEFT TO PATCH, and the hazard goes with it.
|
||||
//
|
||||
// The frontend has already recorded the new binding on the program, and that record is
|
||||
@@ -854,38 +669,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
outIndices[count] = outIndices[0];
|
||||
return true;
|
||||
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
|
||||
if (indexSize == 0 || count < 2) {
|
||||
return false;
|
||||
}
|
||||
const Uint8* indexBytes = nullptr;
|
||||
const auto& vao = *MG_Pipe::gPipeInputs.GetBoundVertexArray();
|
||||
const auto& indexBufferShared = vao.GetIndexBufferBindingSlot().GetBoundObject();
|
||||
if (indexBufferShared != nullptr) {
|
||||
const SizeT offset = reinterpret_cast<SizeT>(indices);
|
||||
const SizeT bufferSize = indexBufferShared->GetSize();
|
||||
if (indexBufferShared->MappedData() == nullptr || offset > bufferSize ||
|
||||
static_cast<SizeT>(count) * indexSize > bufferSize - offset) {
|
||||
return false;
|
||||
}
|
||||
indexBufferShared->SyncPersistentMappedRange();
|
||||
indexBytes = indexBufferShared->MappedData() + offset;
|
||||
} else {
|
||||
indexBytes = static_cast<const Uint8*>(indices);
|
||||
if (indexBytes == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
outIndices.resize(static_cast<SizeT>(count) + 1);
|
||||
for (GLsizei i = 0; i < count; ++i) {
|
||||
switch (indexSize) {
|
||||
case 1: outIndices[i] = indexBytes[i]; break;
|
||||
case 2: outIndices[i] = reinterpret_cast<const Uint16*>(indexBytes)[i]; break;
|
||||
default: outIndices[i] = reinterpret_cast<const Uint32*>(indexBytes)[i]; break;
|
||||
}
|
||||
}
|
||||
outIndices[count] = outIndices[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
|
||||
@@ -807,8 +807,5 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const Bool supported = (properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) != 0;
|
||||
m_wireVertexFormatSupport.emplace(format, supported);
|
||||
return supported;
|
||||
VkFormatProperties properties{};
|
||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &properties);
|
||||
return (properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) != 0;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6583,15 +6583,6 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MG_Backend::gBackendFunctionsTable.GL.GetTextureImage(textureObject, uploadTarget, level, format, type,
|
||||
bufSize, pixels);
|
||||
return;
|
||||
if (MG_Backend::pActiveBackendObject != nullptr &&
|
||||
MG_Backend::pActiveBackendObject->GetBackendType() == BackendType::DirectVulkan &&
|
||||
MGL_BACKEND_SLOT_LOCAL(GetTextureImage)) {
|
||||
MGP_FILL(GetTextureImage);
|
||||
MG_Backend::gBackendFunctionsTable.GL.GetTextureImage(textureObject, uploadTarget, level, format, type,
|
||||
bufSize, pixels);
|
||||
return;
|
||||
}
|
||||
CopyTextureImageToClientOrPBO_State(textureObject, uploadTarget, level, format, type, bufSize, pixels, caller);
|
||||
}
|
||||
|
||||
void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) {
|
||||
@@ -6868,16 +6859,6 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
if (!GetTexImage_State(target, level, format, type, pixels)) return;
|
||||
GetTexImage_Backend(target, level, format, type, pixels);
|
||||
return;
|
||||
if (MGL_BACKEND_SLOT_LOCAL(GetTexImage)) {
|
||||
GetTexImage_Backend(target, level, format, type, pixels);
|
||||
return;
|
||||
}
|
||||
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||
const auto& textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
||||
CopyTextureImageToClientOrPBO_State(textureObject, textureUploadTarget, level, format, type, -1, pixels,
|
||||
__func__);
|
||||
}
|
||||
|
||||
void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) {
|
||||
|
||||
@@ -2263,10 +2263,6 @@ namespace MobileGL::MG_Pipe {
|
||||
|
||||
Bool PipeInputs::HasOpenTransformFeedbackSpan(Uint64 lifetimeId) const {
|
||||
return lifetimeId != 0 && MGPipeApplier().StreamOutputSpans.count(lifetimeId) != 0;
|
||||
|
||||
MGP_STICKY_FORWARD_PULL(HasOpenTransformFeedbackSpan);
|
||||
const auto* ctx = LiveContext();
|
||||
return ctx != nullptr && ctx->HasOpenTransformFeedbackSpan(lifetimeId);
|
||||
}
|
||||
|
||||
void PipeInputs::InvalidateCompileEnv() {
|
||||
|
||||
Reference in New Issue
Block a user