Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 108 additions & 20 deletions src/webgpu/api/operation/command_buffer/queries/occlusionQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ API operations tests for occlusion queries.
- test resolving twice in same pass keeps values
- test resolving twice across pass keeps values
- test resolveQuerySet destinationOffset
- test using the same query multiple times in different passes without resolving in between
`;

import { kUnitCaseParamsBuilder } from '../../../../../common/framework/params_builder.js';
Expand Down Expand Up @@ -436,38 +437,49 @@ class OcclusionQueryTest extends AllFeaturesMaxLimitsGPUTest {
renderMode,
};
}
async runQueryTest(

encodeQueries(
resources: ReturnType<OcclusionQueryTest['setup']>,
renderPassDescriptor: GPURenderPassDescriptor | null,
encodePassFn: (helper: RenderPassHelper, queryIndex: number) => void,
checkQueryIndexResultFn: (passed: boolean, queryIndex: number) => void
encoder: GPUCommandEncoder,
renderPassDescriptor: GPURenderPassDescriptor,
encodePassFn: (helper: RenderPassHelper, queryIndex: number) => void
) {
const { device } = this;
const { occlusionQuerySet, querySetOffset, renderMode = 'direct' } = resources;
const numQueries = occlusionQuerySet.count - querySetOffset;
const queryIndices = range(numQueries, (i: number) => i + querySetOffset);

const pass = encoder.beginRenderPass(renderPassDescriptor);
const helper = new RenderPassHelper(
pass,
renderMode === 'direct'
? new QueryStarterDirect(pass)
: new QueryStarterRenderBundle(device, pass, renderPassDescriptor)
);

for (const queryIndex of queryIndices) {
encodePassFn(helper, queryIndex);
}
pass.end();
}

encodeAndResolveQueries(
resources: ReturnType<OcclusionQueryTest['setup']>,
encoder: GPUCommandEncoder,
renderPassDescriptor: GPURenderPassDescriptor | null,
encodePassFn: (helper: RenderPassHelper, queryIndex: number) => void
) {
const {
readBuffer,
queryResolveBuffer,
queryResolveBufferOffset,
occlusionQuerySet,
querySetOffset,
renderMode = 'direct',
} = resources;
const numQueries = occlusionQuerySet.count - querySetOffset;
const queryIndices = range(numQueries, (i: number) => i + querySetOffset);

const encoder = device.createCommandEncoder();
if (renderPassDescriptor) {
const pass = encoder.beginRenderPass(renderPassDescriptor);
const helper = new RenderPassHelper(
pass,
renderMode === 'direct'
? new QueryStarterDirect(pass)
: new QueryStarterRenderBundle(device, pass, renderPassDescriptor)
);

for (const queryIndex of queryIndices) {
encodePassFn(helper, queryIndex);
}
pass.end();
this.encodeQueries(resources, encoder, renderPassDescriptor, encodePassFn);
}

encoder.resolveQuerySet(
Expand All @@ -484,7 +496,15 @@ class OcclusionQueryTest extends AllFeaturesMaxLimitsGPUTest {
0,
readBuffer.size
);
device.queue.submit([encoder.finish()]);
}

async checkQueryResults(
resources: ReturnType<OcclusionQueryTest['setup']>,
checkQueryIndexResultFn: (passed: boolean, queryIndex: number) => void
) {
const { readBuffer, occlusionQuerySet, querySetOffset } = resources;
const numQueries = occlusionQuerySet.count - querySetOffset;
const queryIndices = range(numQueries, (i: number) => i + querySetOffset);

const result = await this.readBufferAsBigUint64(readBuffer);
for (const queryIndex of queryIndices) {
Expand All @@ -495,6 +515,21 @@ class OcclusionQueryTest extends AllFeaturesMaxLimitsGPUTest {

return result;
}

async runQueryTest(
resources: ReturnType<OcclusionQueryTest['setup']>,
renderPassDescriptor: GPURenderPassDescriptor | null,
encodePassFn: (helper: RenderPassHelper, queryIndex: number) => void,
checkQueryIndexResultFn: (passed: boolean, queryIndex: number) => void
) {
const { device } = this;

const encoder = device.createCommandEncoder();
this.encodeAndResolveQueries(resources, encoder, renderPassDescriptor, encodePassFn);
device.queue.submit([encoder.finish()]);

return this.checkQueryResults(resources, checkQueryIndexResultFn);
}
}

const kQueryTestBaseParams = kUnitCaseParamsBuilder
Expand Down Expand Up @@ -598,6 +633,59 @@ g.test('occlusion_query,basic')
);
});

g.test('occlusion_query,reuse')
.desc(
`
Test queries can be reused.

This tests that if you write to a query twice in the same command buffer (different passes)
and resolve after, that you get the 2nd result.
`
)
.params(kQueryTestBaseParams.combine('noop', ['before', 'after'] as const))
.fn(async t => {
const { writeMask, renderMode, bufferOffset, querySetOffset, noop } = t.params;
const kNumQueries = 30;
const resources = t.setup({
writeMask,
renderMode,
bufferOffset,
querySetOffset,
numQueries: kNumQueries,
});
const { renderPassDescriptor, vertexBuffer, pipeline } = resources;

const opEncodePassFn = (helper: RenderPassHelper, queryIndex: number) => {
const queryHelper = helper.beginOcclusionQuery(queryIndex);
queryHelper.setPipeline(pipeline);
queryHelper.setVertexBuffer(vertexBuffer);
queryHelper.draw(3);
queryHelper.end();
};

const noopEncodePassFn = (helper: RenderPassHelper, queryIndex: number) => {
const queryHelper = helper.beginOcclusionQuery(queryIndex);
queryHelper.end();
};

const [firstOp, secondOp] =
noop === 'before' ? [noopEncodePassFn, opEncodePassFn] : [opEncodePassFn, noopEncodePassFn];

const { device } = t;
const encoder = device.createCommandEncoder();
t.encodeQueries(resources, encoder, renderPassDescriptor, firstOp);
t.encodeAndResolveQueries(resources, encoder, renderPassDescriptor, secondOp);
device.queue.submit([encoder.finish()]);

await t.checkQueryResults(resources, (passed, queryIndex) => {
const expectPassed = noop === 'before';
t.expect(
!!passed === expectPassed,
`queryIndex: ${queryIndex}, was: ${!!passed}, expected: ${expectPassed}`
);
});
});

g.test('occlusion_query,empty')
.desc(
`
Expand Down
Loading