Added ability to read occlusion query result on main thread.

This commit is contained in:
Branimir Karadžić 2015-11-03 16:00:14 -08:00
parent a5bbb18a25
commit ecf7eba043
11 changed files with 111 additions and 39 deletions

View file

@ -212,6 +212,8 @@ class Occlusion : public entry::AppI
bgfx::touch(0); bgfx::touch(0);
bgfx::touch(2); bgfx::touch(2);
uint8_t img[CUBES_DIM*CUBES_DIM*2];
for (uint32_t yy = 0; yy < CUBES_DIM; ++yy) for (uint32_t yy = 0; yy < CUBES_DIM; ++yy)
{ {
for (uint32_t xx = 0; xx < CUBES_DIM; ++xx) for (uint32_t xx = 0; xx < CUBES_DIM; ++xx)
@ -246,9 +248,14 @@ class Occlusion : public entry::AppI
bgfx::setCondition(occlusionQuery, true); bgfx::setCondition(occlusionQuery, true);
bgfx::setState(BGFX_STATE_DEFAULT); bgfx::setState(BGFX_STATE_DEFAULT);
bgfx::submit(2, m_program); bgfx::submit(2, m_program);
img[(yy*CUBES_DIM+xx)*2+0] = " \xfex"[bgfx::getResult(occlusionQuery)];
img[(yy*CUBES_DIM+xx)*2+1] = 0xf;
} }
} }
bgfx::dbgTextImage(5, 5, CUBES_DIM, CUBES_DIM, img, CUBES_DIM*2);
// Advance to next frame. Rendering thread will be kicked to // Advance to next frame. Rendering thread will be kicked to
// process submitted rendering primitives. // process submitted rendering primitives.
bgfx::frame(); bgfx::frame();

View file

@ -275,6 +275,18 @@ namespace bgfx
}; };
}; };
struct OcclusionQueryResult
{
enum Enum
{
Invisible,
Visible,
NoResult,
Count
};
};
static const uint16_t invalidHandle = UINT16_MAX; static const uint16_t invalidHandle = UINT16_MAX;
BGFX_HANDLE(DynamicIndexBufferHandle); BGFX_HANDLE(DynamicIndexBufferHandle);
@ -1538,6 +1550,14 @@ namespace bgfx
/// ///
OcclusionQueryHandle createOcclusionQuery(); OcclusionQueryHandle createOcclusionQuery();
/// Retrieve occlusion query result.
///
/// @param[in] _handle Handle to occlusion query object.
///
/// @attention C99 equivalent is `bgfx_get_result`.
///
OcclusionQueryResult::Enum getResult(OcclusionQueryHandle _handle);
/// Destroy occlusion query. /// Destroy occlusion query.
/// ///
/// @param[in] _handle Handle to occlusion query object. /// @param[in] _handle Handle to occlusion query object.

View file

@ -184,6 +184,16 @@ typedef enum bgfx_backbuffer_ratio
} bgfx_backbuffer_ratio_t; } bgfx_backbuffer_ratio_t;
typedef enum bgfx_occlusion_query_result
{
BGFX_OCCLUSION_QUERY_RESULT_INVISIBLE,
BGFX_OCCLUSION_QUERY_RESULT_VISIBLE,
BGFX_OCCLUSION_QUERY_RESULT_NORESULT,
BGFX_OCCLUSION_QUERY_RESULT_COUNT
} bgfx_occlusion_query_result_t;
#define BGFX_HANDLE_T(_name) \ #define BGFX_HANDLE_T(_name) \
typedef struct _name { uint16_t idx; } _name##_t typedef struct _name { uint16_t idx; } _name##_t
@ -642,6 +652,9 @@ BGFX_C_API void bgfx_destroy_uniform(bgfx_uniform_handle_t _handle);
/**/ /**/
BGFX_C_API bgfx_occlusion_query_handle_t bgfx_create_occlusion_query(); BGFX_C_API bgfx_occlusion_query_handle_t bgfx_create_occlusion_query();
/**/
BGFX_C_API bgfx_occlusion_query_result_t bgfx_get_result(bgfx_occlusion_query_handle_t _handle);
/**/ /**/
BGFX_C_API void bgfx_destroy_occlusion_query(bgfx_occlusion_query_handle_t _handle); BGFX_C_API void bgfx_destroy_occlusion_query(bgfx_occlusion_query_handle_t _handle);

View file

@ -1372,6 +1372,8 @@ namespace bgfx
bx::xchg(m_render, m_submit); bx::xchg(m_render, m_submit);
memcpy(m_render->m_occlusion, m_submit->m_occlusion, sizeof(m_submit->m_occlusion) );
if (!BX_ENABLED(BGFX_CONFIG_MULTITHREADED) if (!BX_ENABLED(BGFX_CONFIG_MULTITHREADED)
|| m_singleThreaded) || m_singleThreaded)
{ {
@ -3026,6 +3028,13 @@ again:
return s_ctx->createOcclusionQuery(); return s_ctx->createOcclusionQuery();
} }
OcclusionQueryResult::Enum getResult(OcclusionQueryHandle _handle)
{
BGFX_CHECK_MAIN_THREAD();
BGFX_CHECK_CAPS(BGFX_CAPS_OCCLUSION_QUERY, "Occlusion query is not supported!");
return s_ctx->getResult(_handle);
}
void destroyOcclusionQuery(OcclusionQueryHandle _handle) void destroyOcclusionQuery(OcclusionQueryHandle _handle)
{ {
BGFX_CHECK_MAIN_THREAD(); BGFX_CHECK_MAIN_THREAD();
@ -3991,6 +4000,12 @@ BGFX_C_API bgfx_occlusion_query_handle_t bgfx_create_occlusion_query()
return handle.c; return handle.c;
} }
BGFX_C_API bgfx_occlusion_query_result_t bgfx_get_result(bgfx_occlusion_query_handle_t _handle)
{
union { bgfx_occlusion_query_handle_t c; bgfx::OcclusionQueryHandle cpp; } handle = { _handle };
return bgfx_occlusion_query_result_t(bgfx::getResult(handle.cpp) );
}
BGFX_C_API void bgfx_destroy_occlusion_query(bgfx_occlusion_query_handle_t _handle) BGFX_C_API void bgfx_destroy_occlusion_query(bgfx_occlusion_query_handle_t _handle)
{ {
union { bgfx_occlusion_query_handle_t c; bgfx::OcclusionQueryHandle cpp; } handle = { _handle }; union { bgfx_occlusion_query_handle_t c; bgfx::OcclusionQueryHandle cpp; } handle = { _handle };

View file

@ -1292,6 +1292,7 @@ namespace bgfx
term.m_program = invalidHandle; term.m_program = invalidHandle;
m_sortKeys[BGFX_CONFIG_MAX_DRAW_CALLS] = term.encodeDraw(); m_sortKeys[BGFX_CONFIG_MAX_DRAW_CALLS] = term.encodeDraw();
m_sortValues[BGFX_CONFIG_MAX_DRAW_CALLS] = BGFX_CONFIG_MAX_DRAW_CALLS; m_sortValues[BGFX_CONFIG_MAX_DRAW_CALLS] = BGFX_CONFIG_MAX_DRAW_CALLS;
memset(m_occlusion, 0xff, sizeof(m_occlusion) );
} }
~Frame() ~Frame()
@ -1681,6 +1682,7 @@ namespace bgfx
Matrix4 m_view[BGFX_CONFIG_MAX_VIEWS]; Matrix4 m_view[BGFX_CONFIG_MAX_VIEWS];
Matrix4 m_proj[2][BGFX_CONFIG_MAX_VIEWS]; Matrix4 m_proj[2][BGFX_CONFIG_MAX_VIEWS];
uint8_t m_viewFlags[BGFX_CONFIG_MAX_VIEWS]; uint8_t m_viewFlags[BGFX_CONFIG_MAX_VIEWS];
uint8_t m_occlusion[BGFX_CONFIG_MAX_OCCUSION_QUERIES];
uint64_t m_sortKeys[BGFX_CONFIG_MAX_DRAW_CALLS+1]; uint64_t m_sortKeys[BGFX_CONFIG_MAX_DRAW_CALLS+1];
RenderItemCount m_sortValues[BGFX_CONFIG_MAX_DRAW_CALLS+1]; RenderItemCount m_sortValues[BGFX_CONFIG_MAX_DRAW_CALLS+1];
@ -3297,9 +3299,27 @@ namespace bgfx
BGFX_API_FUNC(OcclusionQueryHandle createOcclusionQuery() ) BGFX_API_FUNC(OcclusionQueryHandle createOcclusionQuery() )
{ {
OcclusionQueryHandle handle = { m_occlusionQueryHandle.alloc() }; OcclusionQueryHandle handle = { m_occlusionQueryHandle.alloc() };
if (isValid(handle) )
{
m_submit->m_occlusion[handle.idx] = UINT8_MAX;
}
return handle; return handle;
} }
BGFX_API_FUNC(OcclusionQueryResult::Enum getResult(OcclusionQueryHandle _handle) )
{
BGFX_CHECK_HANDLE("destroyOcclusionQuery", m_occlusionQueryHandle, _handle);
switch (m_submit->m_occlusion[_handle.idx])
{
case 0: return OcclusionQueryResult::Invisible;
case UINT8_MAX: return OcclusionQueryResult::NoResult;
default:;
}
return OcclusionQueryResult::Visible;
}
BGFX_API_FUNC(void destroyOcclusionQuery(OcclusionQueryHandle _handle) ) BGFX_API_FUNC(void destroyOcclusionQuery(OcclusionQueryHandle _handle) )
{ {
BGFX_CHECK_HANDLE("destroyOcclusionQuery", m_occlusionQueryHandle, _handle); BGFX_CHECK_HANDLE("destroyOcclusionQuery", m_occlusionQueryHandle, _handle);

View file

@ -2729,10 +2729,10 @@ BX_PRAGMA_DIAGNOSTIC_POP();
return sampler; return sampler;
} }
bool isVisible(OcclusionQueryHandle _handle, bool _visible) bool isVisible(Frame* _render, OcclusionQueryHandle _handle, bool _visible)
{ {
m_occlusionQuery.resolve(); m_occlusionQuery.resolve(_render);
return _visible == m_occlusion[_handle.idx]; return _visible == 0 != _render->m_occlusion[_handle.idx];
} }
DXGI_FORMAT getBufferFormat() DXGI_FORMAT getBufferFormat()
@ -3334,7 +3334,6 @@ BX_PRAGMA_DIAGNOSTIC_POP();
TextureD3D11 m_textures[BGFX_CONFIG_MAX_TEXTURES]; TextureD3D11 m_textures[BGFX_CONFIG_MAX_TEXTURES];
VertexDecl m_vertexDecls[BGFX_CONFIG_MAX_VERTEX_DECLS]; VertexDecl m_vertexDecls[BGFX_CONFIG_MAX_VERTEX_DECLS];
FrameBufferD3D11 m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS]; FrameBufferD3D11 m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS];
bool m_occlusion[BGFX_CONFIG_MAX_OCCUSION_QUERIES];
void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS]; void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS];
Matrix4 m_predefinedUniforms[PredefinedUniform::Count]; Matrix4 m_predefinedUniforms[PredefinedUniform::Count];
UniformRegistry m_uniformReg; UniformRegistry m_uniformReg;
@ -4577,11 +4576,11 @@ BX_PRAGMA_DIAGNOSTIC_POP();
} }
} }
void OcclusionQueryD3D11::begin(OcclusionQueryHandle _handle) void OcclusionQueryD3D11::begin(Frame* _render, OcclusionQueryHandle _handle)
{ {
while (0 == m_control.reserve(1) ) while (0 == m_control.reserve(1) )
{ {
resolve(true); resolve(_render, true);
} }
ID3D11DeviceContext* deviceCtx = s_renderD3D11->m_deviceCtx; ID3D11DeviceContext* deviceCtx = s_renderD3D11->m_deviceCtx;
@ -4598,7 +4597,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
m_control.commit(1); m_control.commit(1);
} }
void OcclusionQueryD3D11::resolve(bool _wait) void OcclusionQueryD3D11::resolve(Frame* _render, bool _wait)
{ {
ID3D11DeviceContext* deviceCtx = s_renderD3D11->m_deviceCtx; ID3D11DeviceContext* deviceCtx = s_renderD3D11->m_deviceCtx;
@ -4613,7 +4612,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
break; break;
} }
s_renderD3D11->m_occlusion[query.m_handle.idx] = 0 < result; _render->m_occlusion[query.m_handle.idx] = 0 < result;
m_control.consume(1); m_control.consume(1);
} }
} }
@ -4687,7 +4686,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
uint32_t statsNumIndices = 0; uint32_t statsNumIndices = 0;
uint32_t statsKeyType[2] = {}; uint32_t statsKeyType[2] = {};
m_occlusionQuery.resolve(); m_occlusionQuery.resolve(_render);
if (0 == (_render->m_debug&BGFX_DEBUG_IFH) ) if (0 == (_render->m_debug&BGFX_DEBUG_IFH) )
{ {
@ -5053,7 +5052,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY); const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY);
if (isValid(draw.m_occlusionQuery) if (isValid(draw.m_occlusionQuery)
&& !hasOcclusionQuery && !hasOcclusionQuery
&& !isVisible(draw.m_occlusionQuery, 0 != (draw.m_submitFlags&BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) ) ) && !isVisible(_render, draw.m_occlusionQuery, 0 != (draw.m_submitFlags&BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) ) )
{ {
continue; continue;
} }
@ -5348,7 +5347,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
if (hasOcclusionQuery) if (hasOcclusionQuery)
{ {
m_occlusionQuery.begin(draw.m_occlusionQuery); m_occlusionQuery.begin(_render, draw.m_occlusionQuery);
} }
if (isValid(draw.m_indirectBuffer) ) if (isValid(draw.m_indirectBuffer) )

View file

@ -316,9 +316,9 @@ namespace bgfx { namespace d3d11
void postReset(); void postReset();
void preReset(); void preReset();
void begin(OcclusionQueryHandle _handle); void begin(Frame* _render, OcclusionQueryHandle _handle);
void end(); void end();
void resolve(bool _wait = false); void resolve(Frame* _render, bool _wait = false);
struct Query struct Query
{ {

View file

@ -1565,10 +1565,10 @@ namespace bgfx { namespace d3d9
} }
} }
bool isVisible(OcclusionQueryHandle _handle, bool _visible) bool isVisible(Frame* _render, OcclusionQueryHandle _handle, bool _visible)
{ {
m_occlusionQuery.resolve(); m_occlusionQuery.resolve(_render);
return _visible == m_occlusion[_handle.idx]; return _visible == 0 != _render->m_occlusion[_handle.idx];
} }
void capturePreReset() void capturePreReset()
@ -2007,7 +2007,6 @@ namespace bgfx { namespace d3d9
TextureD3D9 m_textures[BGFX_CONFIG_MAX_TEXTURES]; TextureD3D9 m_textures[BGFX_CONFIG_MAX_TEXTURES];
VertexDeclD3D9 m_vertexDecls[BGFX_CONFIG_MAX_VERTEX_DECLS]; VertexDeclD3D9 m_vertexDecls[BGFX_CONFIG_MAX_VERTEX_DECLS];
FrameBufferD3D9 m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS]; FrameBufferD3D9 m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS];
bool m_occlusion[BGFX_CONFIG_MAX_OCCUSION_QUERIES];
UniformRegistry m_uniformReg; UniformRegistry m_uniformReg;
void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS]; void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS];
@ -3410,11 +3409,11 @@ namespace bgfx { namespace d3d9
} }
} }
void OcclusionQueryD3D9::begin(OcclusionQueryHandle _handle) void OcclusionQueryD3D9::begin(Frame* _render, OcclusionQueryHandle _handle)
{ {
while (0 == m_control.reserve(1) ) while (0 == m_control.reserve(1) )
{ {
resolve(true); resolve(_render, true);
} }
Query& query = m_query[m_control.m_current]; Query& query = m_query[m_control.m_current];
@ -3429,7 +3428,7 @@ namespace bgfx { namespace d3d9
m_control.commit(1); m_control.commit(1);
} }
void OcclusionQueryD3D9::resolve(bool) void OcclusionQueryD3D9::resolve(Frame* _render, bool)
{ {
while (0 != m_control.available() ) while (0 != m_control.available() )
{ {
@ -3442,7 +3441,7 @@ namespace bgfx { namespace d3d9
break; break;
} }
s_renderD3D9->m_occlusion[query.m_handle.idx] = 0 < result; _render->m_occlusion[query.m_handle.idx] = 0 < result;
m_control.consume(1); m_control.consume(1);
} }
} }
@ -3518,7 +3517,7 @@ namespace bgfx { namespace d3d9
if (m_occlusionQuerySupport) if (m_occlusionQuerySupport)
{ {
m_occlusionQuery.resolve(); m_occlusionQuery.resolve(_render);
} }
if (0 == (_render->m_debug&BGFX_DEBUG_IFH) ) if (0 == (_render->m_debug&BGFX_DEBUG_IFH) )
@ -3539,7 +3538,7 @@ namespace bgfx { namespace d3d9
const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY); const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY);
if (isValid(draw.m_occlusionQuery) if (isValid(draw.m_occlusionQuery)
&& !hasOcclusionQuery && !hasOcclusionQuery
&& !isVisible(draw.m_occlusionQuery, 0 != (draw.m_submitFlags&BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) ) ) && !isVisible(_render, draw.m_occlusionQuery, 0 != (draw.m_submitFlags&BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) ) )
{ {
continue; continue;
} }
@ -4003,7 +4002,7 @@ namespace bgfx { namespace d3d9
if (hasOcclusionQuery) if (hasOcclusionQuery)
{ {
m_occlusionQuery.begin(draw.m_occlusionQuery); m_occlusionQuery.begin(_render, draw.m_occlusionQuery);
} }
if (isValid(draw.m_indexBuffer) ) if (isValid(draw.m_indexBuffer) )

View file

@ -439,9 +439,9 @@ namespace bgfx { namespace d3d9
void postReset(); void postReset();
void preReset(); void preReset();
void begin(OcclusionQueryHandle _handle); void begin(Frame* _render, OcclusionQueryHandle _handle);
void end(); void end();
void resolve(bool _wait = false); void resolve(Frame* _render, bool _wait = false);
struct Query struct Query
{ {

View file

@ -2712,10 +2712,10 @@ namespace bgfx { namespace gl
} }
} }
bool isVisible(OcclusionQueryHandle _handle, bool _visible) bool isVisible(Frame* _render, OcclusionQueryHandle _handle, bool _visible)
{ {
m_occlusionQuery.resolve(); m_occlusionQuery.resolve(_render);
return _visible == m_occlusion[_handle.idx]; return _visible == 0 != _render->m_occlusion[_handle.idx];
} }
void ovrPostReset() void ovrPostReset()
@ -3156,7 +3156,6 @@ namespace bgfx { namespace gl
TextureGL m_textures[BGFX_CONFIG_MAX_TEXTURES]; TextureGL m_textures[BGFX_CONFIG_MAX_TEXTURES];
VertexDecl m_vertexDecls[BGFX_CONFIG_MAX_VERTEX_DECLS]; VertexDecl m_vertexDecls[BGFX_CONFIG_MAX_VERTEX_DECLS];
FrameBufferGL m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS]; FrameBufferGL m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS];
bool m_occlusion[BGFX_CONFIG_MAX_OCCUSION_QUERIES];
UniformRegistry m_uniformReg; UniformRegistry m_uniformReg;
void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS]; void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS];
@ -5166,11 +5165,11 @@ namespace bgfx { namespace gl
} }
} }
void OcclusionQueryGL::begin(OcclusionQueryHandle _handle) void OcclusionQueryGL::begin(Frame* _render, OcclusionQueryHandle _handle)
{ {
while (0 == m_control.reserve(1) ) while (0 == m_control.reserve(1) )
{ {
resolve(true); resolve(_render, true);
} }
Query& query = m_query[m_control.m_current]; Query& query = m_query[m_control.m_current];
@ -5184,7 +5183,7 @@ namespace bgfx { namespace gl
m_control.commit(1); m_control.commit(1);
} }
void OcclusionQueryGL::resolve(bool _wait) void OcclusionQueryGL::resolve(Frame* _render, bool _wait)
{ {
while (0 != m_control.available() ) while (0 != m_control.available() )
{ {
@ -5202,7 +5201,7 @@ namespace bgfx { namespace gl
} }
GL_CHECK(glGetQueryObjectiv(query.m_id, GL_QUERY_RESULT, &result) ); GL_CHECK(glGetQueryObjectiv(query.m_id, GL_QUERY_RESULT, &result) );
s_renderGL->m_occlusion[query.m_handle.idx] = 0 < result; _render->m_occlusion[query.m_handle.idx] = 0 < result;
m_control.consume(1); m_control.consume(1);
} }
} }
@ -5307,7 +5306,7 @@ namespace bgfx { namespace gl
if (m_occlusionQuerySupport) if (m_occlusionQuerySupport)
{ {
m_occlusionQuery.resolve(); m_occlusionQuery.resolve(_render);
} }
if (0 == (_render->m_debug&BGFX_DEBUG_IFH) ) if (0 == (_render->m_debug&BGFX_DEBUG_IFH) )
@ -5605,7 +5604,7 @@ namespace bgfx { namespace gl
const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY); const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY);
if (isValid(draw.m_occlusionQuery) if (isValid(draw.m_occlusionQuery)
&& !hasOcclusionQuery && !hasOcclusionQuery
&& !isVisible(draw.m_occlusionQuery, 0 != (draw.m_submitFlags&BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) ) ) && !isVisible(_render, draw.m_occlusionQuery, 0 != (draw.m_submitFlags&BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) ) )
{ {
continue; continue;
} }
@ -6124,7 +6123,7 @@ namespace bgfx { namespace gl
if (hasOcclusionQuery) if (hasOcclusionQuery)
{ {
m_occlusionQuery.begin(draw.m_occlusionQuery); m_occlusionQuery.begin(_render, draw.m_occlusionQuery);
} }
if (isValid(draw.m_indirectBuffer) ) if (isValid(draw.m_indirectBuffer) )

View file

@ -1270,9 +1270,9 @@ namespace bgfx { namespace gl
void create(); void create();
void destroy(); void destroy();
void begin(OcclusionQueryHandle _handle); void begin(Frame* _render, OcclusionQueryHandle _handle);
void end(); void end();
void resolve(bool _wait = false); void resolve(Frame* _render, bool _wait = false);
struct Query struct Query
{ {