[Refactor] (MG_Backend, MG_Impl/Pipe, MG_IntegrationTest/Harness, MG_Pipe, MG_Pipe/generated, MG_Remote/Client, MG_Test/Pipe, MG_Test/Wire, scripts/data): add by-name mipmap and copy slots that name their texture

- GenerateTextureMipmap and CopyTextureSubImage2D emit the records their target slots emit
- RemoteEmitTable.TheThreeClassesPartitionAllSeventyOneSlots is renamed ...PartitionEverySlot
This commit is contained in:
2026-10-16 14:47:40 -04:00
parent 7dbe0e5c78
commit 28bf0c1653
14 changed files with 128 additions and 30 deletions
+7
View File
@@ -111,11 +111,18 @@ namespace MobileGL {
GLsizei height, GLint border);
void (*CopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
GLsizei width, GLsizei height);
// The by-name form: the destination is `texture`, whatever the units hold. `target` is
// the one the record carries (the texture's own 2D-class target).
void (*CopyTextureSubImage2D)(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target,
GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height);
void (*CopyImageSubData)(const CopyImageEndpoint& src, GLenum srcTarget, GLint srcLevel, GLint srcX,
GLint srcY, GLint srcZ, const CopyImageEndpoint& dst, GLenum dstTarget,
GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth,
GLsizei srcHeight, GLsizei srcDepth);
void (*GenerateMipmap)(GLenum target);
// The by-name form, also used once the bound-target form has resolved its texture.
void (*GenerateTextureMipmap)(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target);
void (*ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels);
void (*GetTexImage)(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
+27 -8
View File
@@ -471,18 +471,21 @@ namespace MobileGL::MG_Record {
EmitF1Clear("ClearNamedFramebufferfi", MG_Pipe::MGPipeFramebufferEmitter::HandleFor(*fbo), buffer,
drawbuffer, MG_Pipe::kMGPipeClearValueClassFloat, nullptr, depth, stencil);
}
// The target-taking slots name the texture through the active unit; the by-name slots pass
// it in. Both build the same record, so the server cannot tell the two apart.
const SharedPtr<MG_State::GLState::ITextureObject>& F1BoundTexture(GLenum target) {
auto& ctx = *MG_State::pGLContext;
return ctx.GetTextureUnitObject(ctx.GetActiveTextureUnit())
.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target))
.GetBoundObject();
}
void EmitF1Copy(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height,
void EmitF1Copy(const char* slot, const SharedPtr<MG_State::GLState::ITextureObject>& destination,
GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height,
GLint xoffset, GLint yoffset, Bool subImage) {
VerbChannel session = ChannelFor(subImage ? "CopyTexSubImage2D" : "CopyTexImage2D");
VerbChannel session = ChannelFor(slot);
BeforeReadOnlyVerb();
MG_Pipe::MGPCopyFromFramebuffer record{};
record.Dst = MG_Pipe::MGPipeTextureEmitterInstance().FindTexture(*F1BoundTexture(target));
record.Dst = MG_Pipe::MGPipeTextureEmitterInstance().FindTexture(*destination);
record.Target = static_cast<Uint32>(target);
record.Level = level;
record.InternalFormat = format;
@@ -498,16 +501,22 @@ namespace MobileGL::MG_Record {
}
void EmitCopyTexImage2D(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width,
GLsizei height, GLint) {
EmitF1Copy(target, level, format, x, y, width, height, 0, 0, false);
EmitF1Copy("CopyTexImage2D", F1BoundTexture(target), target, level, format, x, y, width, height, 0, 0,
false);
}
void EmitCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
GLsizei width, GLsizei height) {
EmitF1Copy(target, level, 0, x, y, width, height, xoffset, yoffset, true);
EmitF1Copy("CopyTexSubImage2D", F1BoundTexture(target), target, level, 0, x, y, width, height, xoffset,
yoffset, true);
}
void EmitGenerateMipmap(GLenum target) {
VerbChannel session = ChannelFor("GenerateMipmap");
void EmitCopyTextureSubImage2D(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target,
GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height) {
EmitF1Copy("CopyTextureSubImage2D", texture, target, level, 0, x, y, width, height, xoffset, yoffset, true);
}
void EmitMipPlan(const char* slot, const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target) {
VerbChannel session = ChannelFor(slot);
BeforeReadOnlyVerb();
const auto& texture = F1BoundTexture(target);
MG_Pipe::MGPMipPlan record{};
record.Res = MG_Pipe::MGPipeTextureEmitterInstance().FindTexture(*texture);
record.Target = static_cast<Uint16>(target);
@@ -523,6 +532,12 @@ namespace MobileGL::MG_Record {
session.EmitAndWait(MG_Pipe::MGPWireOp::GenerateMipmap, &record, sizeof(record), nullptr, 0, nullptr, 0,
nullptr);
}
void EmitGenerateMipmap(GLenum target) {
EmitMipPlan("GenerateMipmap", F1BoundTexture(target), target);
}
void EmitGenerateTextureMipmap(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target) {
EmitMipPlan("GenerateTextureMipmap", texture, target);
}
// All draw entry points share the owned-input preparation and emission below.
[[noreturn]] void RefuseDrawByName(const char* slot, const char* qualifier);
@@ -1639,7 +1654,9 @@ namespace MobileGL::MG_Record {
table.GL.ClearNamedFramebufferuiv = &EmitClearNamedFramebufferuiv;
table.GL.CopyTexImage2D = &EmitCopyTexImage2D;
table.GL.CopyTexSubImage2D = &EmitCopyTexSubImage2D;
table.GL.CopyTextureSubImage2D = &EmitCopyTextureSubImage2D;
table.GL.GenerateMipmap = &EmitGenerateMipmap;
table.GL.GenerateTextureMipmap = &EmitGenerateTextureMipmap;
table.GL.GetTexImage = &EmitGetTexImage;
table.GL.GetTextureImage = &EmitGetTextureImage;
table.GL.BindImageTexture = &EmitBindImageTexture;
@@ -1680,8 +1697,10 @@ namespace MobileGL::MG_Record {
// P13 W4b: the texture family - its record arm resolves the texture from the verb's own
// handle (VerbMipRes, VerbCopyTexDst, the copy's two endpoints).
table.GL.GenerateMipmap = &EmitGenerateMipmap;
table.GL.GenerateTextureMipmap = &EmitGenerateTextureMipmap;
table.GL.CopyTexImage2D = &EmitCopyTexImage2D;
table.GL.CopyTexSubImage2D = &EmitCopyTexSubImage2D;
table.GL.CopyTextureSubImage2D = &EmitCopyTextureSubImage2D;
table.GL.CopyImageSubData = &EmitCopyImageSubData;
table.GL.ReadPixels = &EmitReadPixels;
table.GL.GetTexImage = &EmitGetTexImage;
@@ -69,7 +69,7 @@ namespace MGITest {
bool dataArmIsRecord = false;
// ClientSession::Active() != nullptr.
bool sessionActive = false;
// ImplementedVerbCount(), out of kRemoteEmitSlotCount (71).
// ImplementedVerbCount(), out of kRemoteEmitSlotCount (73).
unsigned int implementedVerbs = 0;
unsigned int totalVerbSlots = 0;
// The encoder's highest produced record ordinal, or 0 when there is no session.
+2
View File
@@ -51,8 +51,10 @@
X(BlitNamedFramebuffer, kBlitOrCopy) \
X(CopyTexImage2D, kBlitOrCopy) \
X(CopyTexSubImage2D, kBlitOrCopy) \
X(CopyTextureSubImage2D, kBlitOrCopy) \
X(CopyImageSubData, kBlitOrCopy) \
X(GenerateMipmap, kTextureOp) \
X(GenerateTextureMipmap, kTextureOp) \
X(ReadPixels, kReadback) \
X(GetTexImage, kReadback) \
X(GetTextureImage, kReadback) \
+2
View File
@@ -56,8 +56,10 @@
X(BlitNamedFramebuffer, Blit) \
X(CopyTexImage2D, CopyFramebufferToTexture) \
X(CopyTexSubImage2D, CopyFramebufferToTexture) \
X(CopyTextureSubImage2D, CopyFramebufferToTexture) \
X(CopyImageSubData, ResourceCopyRegion) \
X(GenerateMipmap, GenerateMipmap) \
X(GenerateTextureMipmap, GenerateMipmap) \
/* read_pixels (reply) or read_pixels_to_buffer (pack buffer): the reply wins. */ \
X(ReadPixels, ReadPixels) \
/* get_texture_image (reply) or its pack-buffer half: the reply wins. */ \
@@ -366,8 +366,10 @@ inline constexpr MGPipeFieldMask kMGPipeAdmittedPullMask[kMGPipeVerbCount] = {
{{0x0000000000000000ull, 0x0000000000000000ull}}, // BlitNamedFramebuffer
{{0x0000000000000000ull, 0x0000000000000000ull}}, // CopyTexImage2D
{{0x0000000000000000ull, 0x0000000000000000ull}}, // CopyTexSubImage2D
{{0x0000000000000000ull, 0x0000000000000000ull}}, // CopyTextureSubImage2D
{{0x0000000000000000ull, 0x0000000000000000ull}}, // CopyImageSubData
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GenerateMipmap
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GenerateTextureMipmap
{{0x0000000000000000ull, 0x0000000000000000ull}}, // ReadPixels
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GetTexImage
{{0x0000000000000000ull, 0x0000000000000000ull}}, // GetTextureImage
@@ -52,8 +52,10 @@ enum class MGPipeVerb : Uint8 {
BlitNamedFramebuffer,
CopyTexImage2D,
CopyTexSubImage2D,
CopyTextureSubImage2D,
CopyImageSubData,
GenerateMipmap,
GenerateTextureMipmap,
ReadPixels,
GetTexImage,
GetTextureImage,
@@ -92,7 +94,7 @@ enum class MGPipeVerb : Uint8 {
};
inline constexpr SizeT kMGPipeVerbCount = static_cast<SizeT>(MGPipeVerb::kVerbCount);
static_assert(kMGPipeVerbCount == 69, "the GLFunctionsTable verb set moved");
static_assert(kMGPipeVerbCount == 71, "the GLFunctionsTable verb set moved");
inline constexpr const char* kMGPipeVerbNames[kMGPipeVerbCount] = {
"DrawArrays",
@@ -128,8 +130,10 @@ inline constexpr const char* kMGPipeVerbNames[kMGPipeVerbCount] = {
"BlitNamedFramebuffer",
"CopyTexImage2D",
"CopyTexSubImage2D",
"CopyTextureSubImage2D",
"CopyImageSubData",
"GenerateMipmap",
"GenerateTextureMipmap",
"ReadPixels",
"GetTexImage",
"GetTextureImage",
@@ -228,8 +232,10 @@ inline constexpr MGPipeVerbClass kMGPipeVerbClass[kMGPipeVerbCount] = {
MGPipeVerbClass::kBlitOrCopy, // BlitNamedFramebuffer
MGPipeVerbClass::kBlitOrCopy, // CopyTexImage2D
MGPipeVerbClass::kBlitOrCopy, // CopyTexSubImage2D
MGPipeVerbClass::kBlitOrCopy, // CopyTextureSubImage2D
MGPipeVerbClass::kBlitOrCopy, // CopyImageSubData
MGPipeVerbClass::kTextureOp, // GenerateMipmap
MGPipeVerbClass::kTextureOp, // GenerateTextureMipmap
MGPipeVerbClass::kReadback, // ReadPixels
MGPipeVerbClass::kReadback, // GetTexImage
MGPipeVerbClass::kReadback, // GetTextureImage
+6 -6
View File
@@ -67,7 +67,7 @@ namespace MobileGL::MG_Remote::Client {
using MG_Record::LogE2ControlLine;
// The slot arithmetic, asserted rather than commented. GlobalBackendFunctionsTable is
// GLFunctionsTable plus Present plus SetSwapInterval; GLFunctionsTable is 69 function
// GLFunctionsTable plus Present plus SetSwapInterval; GLFunctionsTable is 71 function
// pointers plus one Bool (PrefersCpuXfbPrimitiveAccounting). A slot
// added to either without a decision here is a build break, which is the point: R-4 forbids
// a null slot, so a new slot needs an owner on the day it appears.
@@ -76,7 +76,7 @@ namespace MobileGL::MG_Remote::Client {
"GlobalBackendFunctionsTable is no longer GLFunctionsTable + Present + SetSwapInterval");
static_assert(sizeof(MG_Backend::GlobalBackendFunctionsTable) ==
kRemoteEmitSlotCount * sizeof(void (*)()) + sizeof(void (*)()),
"the emit table's 71 slots plus the packed Bool no longer describe the table");
"the emit table's 73 slots plus the packed Bool no longer describe the table");
namespace {
@@ -558,7 +558,7 @@ namespace MobileGL::MG_Remote::Client {
constexpr Uint32 kEmittedSlotsD1 = 19;
constexpr Uint32 kEmittedSlotsI1 = 7;
constexpr Uint32 kEmittedSlotsT2 = 7;
constexpr Uint32 kEmittedSlotsF1 = 11;
constexpr Uint32 kEmittedSlotsF1 = 13;
// BlitNamedFramebuffer, both texture readbacks, and SetSwapInterval (P10 B) - the one
// class-B slot that crosses as a surface control frame rather than a SEG_CMD record.
constexpr Uint32 kEmittedSlotsTail = 4;
@@ -575,12 +575,12 @@ namespace MobileGL::MG_Remote::Client {
static_assert(kUnmigratedD1 + kEmittedSlotsD1 == 19, "d1 owns the 19 draw slots");
static_assert(kUnmigratedI1 + kEmittedSlotsI1 == 7, "i1 owns the 7 image/compute/barrier/copy/SSBO slots");
static_assert(kUnmigratedT2 + kEmittedSlotsT2 == 7, "t2 owns the 7 XFB/tessellation slots");
static_assert(kUnmigratedF1 + kEmittedSlotsF1 == 11, "f1 owns the 11 clear/copy/mip slots");
static_assert(kUnmigratedF1 + kEmittedSlotsF1 == 13, "f1 owns the 13 clear/copy/mip slots");
static_assert(kUnmigratedTail + kEmittedSlotsTail + kEmittedSlotsSync + kEmittedSlotsQueries == 20,
"the original wave-3 tail owns 20 slots");
static_assert(kUnmigratedSlots + kEmittedSlots == 69, "class B and C own 69 slots");
static_assert(kUnmigratedSlots + kEmittedSlots == 71, "class B and C own 71 slots");
static_assert(kLocallyAnsweredSlots + kEmittedSlots + kUnmigratedSlots == kRemoteEmitSlotCount,
"the three classes no longer partition the 71 slots");
"the three classes no longer partition the 73 slots");
MG_Backend::GlobalBackendFunctionsTable BuildRemoteEmitTable() {
MG_Record::ArmVerbControlKnobs();
+2 -2
View File
@@ -69,14 +69,14 @@ namespace MobileGL::MG_Remote::Client {
using MG_Record::TightReadbackByteCount;
using MG_Record::UnmigratedVerbFatal;
// How many of the 71 slots have a real emitter. Reported at bring-up and asserted by the
// How many of the 73 slots have a real emitter. Reported at bring-up and asserted by the
// gate: a table that silently loses an emitter should not be able to look the same as one
// that never had it.
Uint32 ImplementedVerbCount();
// The total the count above is out of. Asserted against the struct in EmitTables.cpp, so
// a slot added to GLFunctionsTable without a decision here is a build break.
inline constexpr Uint32 kRemoteEmitSlotCount = 71;
inline constexpr Uint32 kRemoteEmitSlotCount = 73;
// The other two thirds of the census, so a case can assert the WHOLE partition rather than
// only the half that emits. CONTRACT-P5.md §7's three classes are 2 + 5 + 64, and
+3 -2
View File
@@ -938,10 +938,10 @@ TEST(PipeCatalogue, PipeInputFieldsStartUnfilled) {
EXPECT_FALSE(MGPipeInputFieldIsFresh(state, MGPipeInputField::GetRenderStateParameters));
}
// G5b: the verb enum is GLFunctionsTable's member list (69 entries), every class has verbs,
// G5b: the verb enum is GLFunctionsTable's member list (71 entries), every class has verbs,
// and the seven sticky fields ride in every class mask (P1 brief D7).
TEST(PipeCatalogue, VerbTableIsTheFunctionTable) {
EXPECT_EQ(kMGPipeVerbCount, 69u);
EXPECT_EQ(kMGPipeVerbCount, 71u);
EXPECT_EQ(kMGPipeVerbClassCount, 9u);
SizeT perClass[kMGPipeVerbClassCount] = {};
for (SizeT v = 0; v < kMGPipeVerbCount; ++v) {
@@ -964,6 +964,7 @@ TEST(PipeCatalogue, VerbTableIsTheFunctionTable) {
EXPECT_FALSE(MGPipeFieldMaskHas(query, MGPipeInputField::GetRenderStateParameters));
EXPECT_TRUE(MGPipeFieldMaskHas(query, MGPipeInputField::GetTransformFeedbackPausedPrimitiveCounter));
EXPECT_EQ(kMGPipeVerbClass[static_cast<SizeT>(MGPipeVerb::GenerateMipmap)], MGPipeVerbClass::kTextureOp);
EXPECT_EQ(kMGPipeVerbClass[static_cast<SizeT>(MGPipeVerb::GenerateTextureMipmap)], MGPipeVerbClass::kTextureOp);
EXPECT_STREQ(kMGPipeVerbNames[static_cast<SizeT>(MGPipeVerb::GetGpuTimestampNs)], "GetGpuTimestampNs");
}
+3 -3
View File
@@ -359,7 +359,7 @@ TEST_F(PipeInputsTest, MutatedFieldIsNamedAtRead) {
::_exit(3);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed - 63 fields, 69 verbs, fatal=1"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed - 63 fields, 71 verbs, fatal=1"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify read of GetLineWidth (index 0, 0) differs from the live context"),
std::string::npos)
<< r.Log;
@@ -389,7 +389,7 @@ TEST_F(PipeInputsTest, VerifyCorruptKnobNamesTheFieldAtEntry) {
::_exit(3);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed - 63 fields, 69 verbs, fatal=1"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed - 63 fields, 71 verbs, fatal=1"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify corruption armed - GetRenderStateParameters"), std::string::npos) << r.Log;
const std::string expected =
"Fatal{PipeVerifyDiffer, \"GetRenderStateParameters@DrawArrays\", verb=" + std::to_string(serial) +
@@ -445,7 +445,7 @@ TEST_F(PipeInputsTest, VerifyFatalOffLogsTheDivergenceAndContinues) {
std::exit(0);
});
ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed - 63 fields, 69 verbs, fatal=0"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify armed - 63 fields, 71 verbs, fatal=0"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MGPipe: verify summary - 2 divergence(s) survived MOBILEGL_PIPE_VERIFY_FATAL=0"),
std::string::npos)
<< r.Log;
+1 -1
View File
@@ -209,7 +209,7 @@ gtest_discover_tests(PipeWireCodecTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS un
# main() of its own. All three also reach MG_Pipe, MG_Backend and MG_State, so all three carry
# those include paths.
#
# RemoteClientTest the 71-slot emit table, the caps mirror, R-8's liveness gates, R-17's
# RemoteClientTest the 73-slot emit table, the caps mirror, R-8's liveness gates, R-17's
# routing and ID-47/ID-49's readback rules (c1)
# ServerLoopTest the apply thread, the blocking control mailbox, the verb stamp and
# R-11's server-owned staging copy (v1)
+64 -5
View File
@@ -6,7 +6,7 @@
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// P5 package c1's suite: the 71-slot emit table, the caps mirror and R-8's liveness gates.
// P5 package c1's suite: the 73-slot emit table, the caps mirror and R-8's liveness gates.
// The helper cases below are supplemented by RemoteClientControls.inc: installed producers
// over a live session, with adversarial peer replies and a repeated-make-current control.
//
@@ -192,11 +192,11 @@ namespace {
// The emit table: the partition, and that no slot is null
// =====================================================================================
TEST(RemoteEmitTable, TheThreeClassesPartitionAllSeventyOneSlots) {
// P5 baseline five + f1 eleven + i1 seven + t2 six emitted slots, ... and since P10 B the
TEST(RemoteEmitTable, TheThreeClassesPartitionEverySlot) {
// P5 baseline five + f1 thirteen + i1 seven + t2 six emitted slots, ... and since P10 B the
// swap interval: class C is EMPTY, every slot but the two local answers reaches the server.
EXPECT_EQ(LocallyAnsweredSlotCount(), 2u);
EXPECT_EQ(ImplementedVerbCount(), 69u);
EXPECT_EQ(ImplementedVerbCount(), 71u);
EXPECT_EQ(UnmigratedSlotCount(), 0u);
EXPECT_EQ(LocallyAnsweredSlotCount() + ImplementedVerbCount() + UnmigratedSlotCount(), kRemoteEmitSlotCount);
}
@@ -298,7 +298,7 @@ TEST(RemoteEmitTable, NoSlotIsNull) {
const MG_Backend::GlobalBackendFunctionsTable& table = RemoteEmitTable();
const void* const* cells = reinterpret_cast<const void* const*>(&table);
const SizeT cellCount = sizeof(table) / sizeof(void*);
// The struct is 71 function pointers plus ONE cell holding the packed Bool
// The struct is 73 function pointers plus ONE cell holding the packed Bool
// PrefersCpuXfbPrimitiveAccounting and its padding (EmitTables.cpp static_asserts exactly
// that shape). That Bool is legitimately zero when the server did not publish
// kCapCpuXfbPrimitiveAccounting, so at most one cell may read null - and this is stated as
@@ -1981,6 +1981,65 @@ TEST(RemoteF1, GenerateMipmapFieldsCross) {
EXPECT_TRUE(WIFEXITED(child.Status) && WEXITSTATUS(child.Status) == 0)
<< "F1.GenerateMipmap.fields: " << DescribeStatus(child) << child.Log;
}
namespace {
// A second texture for the by-name slots, acquired but NOT bound: F1Texture's stays on the unit.
SharedPtr<MG_State::GLState::ITextureObject> F1NamedTexture() {
auto tex = MakeShared<MG_State::GLState::TextureObject2D>(92);
tex->SetInternalFormat(TextureInternalFormat::RGBA8);
for (Uint level = 0; level != 2; ++level) {
const Int size = 4 >> level;
tex->AllocateStorage(
TextureUploadTarget::Texture2D, level,
MG_State::GLState::MipmapInput{IntVec3{size, size, 1}, static_cast<SizeT>(size * size * 4)});
}
MGPipeTextureEmitterInstance().AcquireTexture(tex->GetLifetimeId(), tex.get());
return tex;
}
} // namespace
// The by-name slots name their texture themselves: the record carries the named texture's handle
// while another texture stays bound on the active unit.
TEST(RemoteF1, CopyTextureSubImage2DNamesItsTextureNotTheBoundOne) {
const auto child = RunInChild([] {
StartControlSession();
F1Peer peer;
peer.Install();
const auto bound = F1Texture();
const auto named = F1NamedTexture();
RemoteEmitTable().GL.CopyTextureSubImage2D(named, GL_TEXTURE_2D, 1, 1, 0, -3, 4, 1, 2);
const auto& r = peer.copy;
if (peer.calls != 1 || r.Dst != MGPipeTextureEmitterInstance().FindTexture(*named) ||
MGPipeHandleIsNull(r.Dst) || r.Dst == MGPipeTextureEmitterInstance().FindTexture(*bound) ||
r.Target != GL_TEXTURE_2D || r.Level != 1 || r.XOffset != 1 || r.YOffset != 0 || r.X != -3 || r.Y != 4 ||
r.Width != 1 || r.Height != 2 || r.SubImage != 1)
::_exit(101);
ClientSessionInstance().Stop();
});
EXPECT_TRUE(WIFEXITED(child.Status) && WEXITSTATUS(child.Status) == 0)
<< "F1.CopyTextureSubImage2D.fields: " << DescribeStatus(child) << child.Log;
}
TEST(RemoteF1, GenerateTextureMipmapNamesItsTextureNotTheBoundOne) {
const auto child = RunInChild([] {
StartControlSession();
F1Peer peer;
peer.Install();
const auto bound = F1Texture();
const auto named = F1NamedTexture();
RemoteEmitTable().GL.GenerateTextureMipmap(named, GL_TEXTURE_2D);
const GLfloat fence[4] = {0, 0, 0, 0}; // see GenerateMipmapFieldsCross
RemoteEmitTable().GL.ClearBufferfv(GL_COLOR, 0, fence);
const auto& r = peer.mip;
if (peer.calls != 2 || r.Res != MGPipeTextureEmitterInstance().FindTexture(*named) ||
MGPipeHandleIsNull(r.Res) || r.Res == MGPipeTextureEmitterInstance().FindTexture(*bound) ||
r.Target != GL_TEXTURE_2D || r.BaseLevel != 0 || r.LevelCount != 2)
::_exit(101);
ClientSessionInstance().Stop();
});
EXPECT_TRUE(WIFEXITED(child.Status) && WEXITSTATUS(child.Status) == 0)
<< "F1.GenerateTextureMipmap.fields: " << DescribeStatus(child) << child.Log;
}
#endif
#if MGTEST_HAVE_FORK
+1 -1
View File
@@ -9121,7 +9121,7 @@
"RemoteEmitTable.TheNineteenDrawSlotsAreEmittersDistinctFromEveryStub",
"RemoteEmitTable.TheSevenI1SlotsAreClassBAndAreNotTheFatalThunk",
"RemoteEmitTable.TheSixXfbAndPatchSlotsAreNonNullAndDistinct",
"RemoteEmitTable.TheThreeClassesPartitionAllSeventyOneSlots",
"RemoteEmitTable.TheThreeClassesPartitionEverySlot",
"RemoteF1.AnIndirectDrawStashesTheCommandAndParameterBuffers",
"RemoteF1.BlitNamedCarriesHandlesAndLeavesTheClientBindingsAlone",
"RemoteF1.BoundBlitCarriesNoHandlesAndIsAppliedAsBefore",