Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Laszlo Agocs
qtrhi
Commits
b4cfad8c
Commit
b4cfad8c
authored
Dec 10, 2018
by
Laszlo Agocs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mtl: Add support for readbacks and offscreen frames
parent
731c3705
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
410 additions
and
28 deletions
+410
-28
examples/rhi/offscreen_metal/main.cpp
examples/rhi/offscreen_metal/main.cpp
+209
-0
examples/rhi/offscreen_metal/offscreen_metal.pro
examples/rhi/offscreen_metal/offscreen_metal.pro
+12
-0
examples/rhi/offscreen_metal/offscreen_metal.qrc
examples/rhi/offscreen_metal/offscreen_metal.qrc
+6
-0
examples/rhi/rhi.pro
examples/rhi/rhi.pro
+2
-1
examples/rhi/shared/examplewindow.cpp
examples/rhi/shared/examplewindow.cpp
+6
-1
src/rhi/qrhi.h
src/rhi/qrhi.h
+12
-9
src/rhi/qrhimetal.mm
src/rhi/qrhimetal.mm
+157
-13
src/rhi/qrhimetal_p.h
src/rhi/qrhimetal_p.h
+2
-0
src/rhi/qrhivulkan.cpp
src/rhi/qrhivulkan.cpp
+1
-1
todo.txt
todo.txt
+3
-3
No files found.
examples/rhi/offscreen_metal/main.cpp
0 → 100644
View file @
b4cfad8c
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QGuiApplication>
#include <QImage>
#include <QFileInfo>
#include <QFile>
#include <QBakedShader>
#include <QRhiMetalInitParams>
static
float
vertexData
[]
=
{
// Y up (note m_proj), CCW
0.0
f
,
0.5
f
,
1.0
f
,
0.0
f
,
0.0
f
,
-
0.5
f
,
-
0.5
f
,
0.0
f
,
1.0
f
,
0.0
f
,
0.5
f
,
-
0.5
f
,
0.0
f
,
0.0
f
,
1.0
f
,
};
static
QBakedShader
getShader
(
const
QString
&
name
)
{
QFile
f
(
name
);
if
(
f
.
open
(
QIODevice
::
ReadOnly
))
return
QBakedShader
::
fromSerialized
(
f
.
readAll
());
return
QBakedShader
();
}
int
main
(
int
argc
,
char
**
argv
)
{
QCoreApplication
::
setAttribute
(
Qt
::
AA_EnableHighDpiScaling
);
QGuiApplication
app
(
argc
,
argv
);
QRhiMetalInitParams
params
;
QRhi
*
r
=
QRhi
::
create
(
QRhi
::
Metal
,
&
params
);
if
(
!
r
)
{
qWarning
(
"Failed to initialize RHI"
);
return
1
;
}
QRhiTexture
*
tex
=
r
->
newTexture
(
QRhiTexture
::
RGBA8
,
QSize
(
1280
,
720
),
1
,
QRhiTexture
::
RenderTarget
|
QRhiTexture
::
UsedAsTransferSource
);
tex
->
build
();
QRhiTextureRenderTarget
*
rt
=
r
->
newTextureRenderTarget
({
tex
});
QRhiRenderPassDescriptor
*
rp
=
rt
->
newCompatibleRenderPassDescriptor
();
rt
->
setRenderPassDescriptor
(
rp
);
rt
->
build
();
QMatrix4x4
proj
=
r
->
clipSpaceCorrMatrix
();
proj
.
perspective
(
45.0
f
,
1280
/
720.
f
,
0.01
f
,
1000.0
f
);
proj
.
translate
(
0
,
0
,
-
4
);
QRhiBuffer
*
vbuf
=
r
->
newBuffer
(
QRhiBuffer
::
Immutable
,
QRhiBuffer
::
VertexBuffer
,
sizeof
(
vertexData
));
vbuf
->
build
();
QRhiBuffer
*
ubuf
=
r
->
newBuffer
(
QRhiBuffer
::
Dynamic
,
QRhiBuffer
::
UniformBuffer
,
68
);
ubuf
->
build
();
QRhiShaderResourceBindings
*
srb
=
r
->
newShaderResourceBindings
();
srb
->
setBindings
({
QRhiShaderResourceBinding
::
uniformBuffer
(
0
,
QRhiShaderResourceBinding
::
VertexStage
|
QRhiShaderResourceBinding
::
FragmentStage
,
ubuf
)
});
srb
->
build
();
QRhiGraphicsPipeline
*
ps
=
r
->
newGraphicsPipeline
();
QRhiGraphicsPipeline
::
TargetBlend
premulAlphaBlend
;
premulAlphaBlend
.
enable
=
true
;
ps
->
setTargetBlends
({
premulAlphaBlend
});
const
QBakedShader
vs
=
getShader
(
QLatin1String
(
":/color.vert.qsb"
));
if
(
!
vs
.
isValid
())
qFatal
(
"Failed to load shader pack (vertex)"
);
const
QBakedShader
fs
=
getShader
(
QLatin1String
(
":/color.frag.qsb"
));
if
(
!
fs
.
isValid
())
qFatal
(
"Failed to load shader pack (fragment)"
);
ps
->
setShaderStages
({
{
QRhiGraphicsShaderStage
::
Vertex
,
vs
},
{
QRhiGraphicsShaderStage
::
Fragment
,
fs
}
});
QRhiVertexInputLayout
inputLayout
;
inputLayout
.
bindings
=
{
{
5
*
sizeof
(
float
)
}
};
inputLayout
.
attributes
=
{
{
0
,
0
,
QRhiVertexInputLayout
::
Attribute
::
Float2
,
0
},
{
0
,
1
,
QRhiVertexInputLayout
::
Attribute
::
Float3
,
2
*
sizeof
(
float
)
}
};
ps
->
setVertexInputLayout
(
inputLayout
);
ps
->
setShaderResourceBindings
(
srb
);
ps
->
setRenderPassDescriptor
(
rp
);
ps
->
build
();
for
(
int
frame
=
0
;
frame
<
20
;
++
frame
)
{
QRhiCommandBuffer
*
cb
;
if
(
r
->
beginOffscreenFrame
(
&
cb
)
!=
QRhi
::
FrameOpSuccess
)
{
qDebug
(
"Failed to start frame"
);
break
;
}
qDebug
(
"Generating offscreen frame %d"
,
frame
);
QRhiResourceUpdateBatch
*
u
=
r
->
nextResourceUpdateBatch
();
if
(
frame
==
0
)
u
->
uploadStaticBuffer
(
vbuf
,
vertexData
);
static
float
rotation
=
0.0
f
;
QMatrix4x4
mvp
=
proj
;
mvp
.
rotate
(
rotation
,
0
,
1
,
0
);
u
->
updateDynamicBuffer
(
ubuf
,
0
,
64
,
mvp
.
constData
());
rotation
+=
5.0
f
;
static
float
opacity
=
1.0
f
;
static
int
opacityDir
=
1
;
u
->
updateDynamicBuffer
(
ubuf
,
64
,
4
,
&
opacity
);
opacity
+=
opacityDir
*
0.005
f
;
if
(
opacity
<
0.0
f
||
opacity
>
1.0
f
)
{
opacityDir
*=
-
1
;
opacity
=
qBound
(
0.0
f
,
opacity
,
1.0
f
);
}
cb
->
beginPass
(
rt
,
{
0
,
1
,
0
,
1
},
{
1
,
0
},
u
);
cb
->
setGraphicsPipeline
(
ps
);
cb
->
setViewport
({
0
,
0
,
1280
,
720
});
cb
->
setVertexInput
(
0
,
{
{
vbuf
,
0
}
});
cb
->
draw
(
3
);
u
=
r
->
nextResourceUpdateBatch
();
QRhiReadbackDescription
rb
(
tex
);
QRhiReadbackResult
rbResult
;
rbResult
.
completed
=
[
frame
]
{
qDebug
(
" - readback %d completed"
,
frame
);
};
u
->
readBackTexture
(
rb
,
&
rbResult
);
cb
->
endPass
(
u
);
qDebug
(
"Submit and wait"
);
r
->
endOffscreenFrame
();
// No finish() or waiting for the completed callback is needed here
// since the endOffscreenFrame() implies a wait for completion.
if
(
!
rbResult
.
data
.
isEmpty
())
{
const
uchar
*
p
=
reinterpret_cast
<
const
uchar
*>
(
rbResult
.
data
.
constData
());
QImage
image
(
p
,
rbResult
.
pixelSize
.
width
(),
rbResult
.
pixelSize
.
height
(),
QImage
::
Format_RGBA8888
);
QString
fn
=
QString
::
asprintf
(
"frame%d.png"
,
frame
);
fn
=
QFileInfo
(
fn
).
absoluteFilePath
();
qDebug
(
"Saving into %s"
,
qPrintable
(
fn
));
image
.
save
(
fn
);
}
else
{
qWarning
(
"Readback failed!"
);
}
}
ps
->
releaseAndDestroy
();
srb
->
releaseAndDestroy
();
ubuf
->
releaseAndDestroy
();
vbuf
->
releaseAndDestroy
();
rt
->
releaseAndDestroy
();
rp
->
releaseAndDestroy
();
tex
->
releaseAndDestroy
();
delete
r
;
return
0
;
}
examples/rhi/offscreen_metal/offscreen_metal.pro
0 → 100644
View file @
b4cfad8c
TEMPLATE
=
app
CONFIG
+=
console
QT
+=
shadertools
rhi
SOURCES
=
\
main
.
cpp
RESOURCES
=
offscreen_metal
.
qrc
target
.
path
=
$$
[
QT_INSTALL_EXAMPLES
]
/
rhi
/
offscreen_metal
INSTALLS
+=
target
examples/rhi/offscreen_metal/offscreen_metal.qrc
0 → 100644
View file @
b4cfad8c
<!DOCTYPE RCC>
<RCC
version=
"1.0"
>
<qresource>
<file
alias=
"color.vert.qsb"
>
../shared/color.vert.qsb
</file>
<file
alias=
"color.frag.qsb"
>
../shared/color.frag.qsb
</file>
</qresource>
</RCC>
examples/rhi/rhi.pro
View file @
b4cfad8c
...
...
@@ -25,5 +25,6 @@ win32 {
mac
{
SUBDIRS
+=
\
plainqwindow_metal
plainqwindow_metal
\
offscreen_metal
}
examples/rhi/shared/examplewindow.cpp
View file @
b4cfad8c
...
...
@@ -114,9 +114,14 @@ void ExampleWindow::init()
m_sc
->
setWindow
(
this
);
m_sc
->
setDepthStencil
(
m_ds
);
m_sc
->
setSampleCount
(
m_sampleCount
);
QRhiSwapChain
::
Flags
scFlags
=
0
;
#ifdef READBACK_SWAPCHAIN
scFlags
|=
QRhiSwapChain
::
UsedAsTransferSource
;
#endif
#ifdef USE_SRGB_SWAPCHAIN
m_sc
->
set
Flags
(
QRhiSwapChain
::
sRGB
)
;
sc
Flags
|=
QRhiSwapChain
::
sRGB
;
#endif
m_sc
->
setFlags
(
scFlags
);
m_scrp
=
m_sc
->
newCompatibleRenderPassDescriptor
();
m_sc
->
setRenderPassDescriptor
(
m_scrp
);
...
...
src/rhi/qrhi.h
View file @
b4cfad8c
...
...
@@ -835,18 +835,19 @@ Q_DECLARE_TYPEINFO(QRhiGraphicsPipeline::TargetBlend, Q_MOVABLE_TYPE);
class
Q_RHI_EXPORT
QRhiSwapChain
:
public
QRhiResource
{
public:
enum
SurfaceImport
Flag
{
enum
Flag
{
SurfaceHasPreMulAlpha
=
1
<<
0
,
SurfaceHasNonPreMulAlpha
=
1
<<
1
,
sRGB
=
1
<<
2
sRGB
=
1
<<
2
,
UsedAsTransferSource
=
1
<<
3
// will be read back
};
Q_DECLARE_FLAGS
(
SurfaceImportFlags
,
SurfaceImport
Flag
)
Q_DECLARE_FLAGS
(
Flags
,
Flag
)
QWindow
*
window
()
const
{
return
m_window
;
}
void
setWindow
(
QWindow
*
window
)
{
m_window
=
window
;
}
SurfaceImport
Flags
flags
()
const
{
return
m_flags
;
}
void
setFlags
(
SurfaceImport
Flags
f
)
{
m_flags
=
f
;
}
Flags
flags
()
const
{
return
m_flags
;
}
void
setFlags
(
Flags
f
)
{
m_flags
=
f
;
}
QRhiRenderBuffer
*
depthStencil
()
const
{
return
m_depthStencil
;
}
void
setDepthStencil
(
QRhiRenderBuffer
*
ds
)
{
m_depthStencil
=
ds
;
}
...
...
@@ -896,7 +897,7 @@ public:
protected:
QRhiSwapChain
(
QRhiImplementation
*
rhi
);
QWindow
*
m_window
=
nullptr
;
SurfaceImport
Flags
m_flags
;
Flags
m_flags
;
QRhiRenderBuffer
*
m_depthStencil
=
nullptr
;
int
m_sampleCount
=
1
;
QRhiRenderPassDescriptor
*
m_renderPassDesc
=
nullptr
;
...
...
@@ -905,7 +906,7 @@ protected:
void
*
m_reserved
;
};
Q_DECLARE_OPERATORS_FOR_FLAGS
(
QRhiSwapChain
::
SurfaceImport
Flags
)
Q_DECLARE_OPERATORS_FOR_FLAGS
(
QRhiSwapChain
::
Flags
)
class
Q_RHI_EXPORT
QRhiCommandBuffer
:
public
QRhiResource
{
...
...
@@ -1159,8 +1160,10 @@ public:
is to use it in completely offscreen applications, e.g. to generate image
sequences by rendering and reading back without ever showing a window.
Usage in on-screen applications (so beginFrame, endFrame,
beginOffscreenFrame, endOffscreenFrame, beginFrame, ...) is possible too but
it does break parallelism so should be done only infrequently.
beginOffscreenFrame, endOffscreenFrame, beginFrame, ...) is possible too
but it does reduce parallelism (offscreen frames do not let the CPU -
potentially - generate another frame while the GPU is still processing
the previous one) so should be done only infrequently.
QRhiReadbackResult rbResult;
QRhiCommandBuffer *cb; // not owned
beginOffscreenFrame(&cb);
...
...
src/rhi/qrhimetal.mm
View file @
b4cfad8c
...
...
@@ -65,6 +65,8 @@ QT_BEGIN_NAMESPACE
struct
QRhiMetalData
{
QRhiMetalData
(
QRhiImplementation
*
rhi
)
:
ofr
(
rhi
)
{
}
id
<
MTLDevice
>
dev
;
id
<
MTLCommandQueue
>
cmdQueue
;
...
...
@@ -104,8 +106,28 @@ struct QRhiMetalData
};
};
QVector
<
DeferredReleaseEntry
>
releaseQueue
;
struct
OffscreenFrame
{
OffscreenFrame
(
QRhiImplementation
*
rhi
)
:
cbWrapper
(
rhi
)
{
}
bool
active
=
false
;
QMetalCommandBuffer
cbWrapper
;
}
ofr
;
struct
ActiveReadback
{
int
activeFrameSlot
=
-
1
;
QRhiReadbackDescription
desc
;
QRhiReadbackResult
*
result
;
id
<
MTLBuffer
>
buf
;
quint32
bufSize
;
QSize
pixelSize
;
QRhiTexture
::
Format
format
;
};
QVector
<
ActiveReadback
>
activeReadbacks
;
};
Q_DECLARE_TYPEINFO
(
QRhiMetalData
::
DeferredReleaseEntry
,
Q_MOVABLE_TYPE
);
Q_DECLARE_TYPEINFO
(
QRhiMetalData
::
ActiveReadback
,
Q_MOVABLE_TYPE
);
struct
QMetalBufferData
{
bool
managed
;
...
...
@@ -143,15 +165,17 @@ struct QMetalRenderTargetData
QSize
pixelSize
;
int
colorAttCount
=
0
;
int
dsAttCount
=
0
;
struct
ColorAtt
{
id
<
MTLTexture
>
tex
=
nil
;
int
layer
=
0
;
int
level
=
0
;
id
<
MTLTexture
>
resolveTex
=
nil
;
int
resolveLayer
=
0
;
int
resolveLevel
=
0
;
};
struct
{
struct
ColorAtt
{
id
<
MTLTexture
>
tex
=
nil
;
int
layer
=
0
;
int
level
=
0
;
id
<
MTLTexture
>
resolveTex
=
nil
;
int
resolveLayer
=
0
;
int
resolveLevel
=
0
;
};
ColorAtt
colorAtt
[
QMetalRenderPassDescriptor
:
:
MAX_COLOR_ATTACHMENTS
];
id
<
MTLTexture
>
dsTex
=
nil
;
bool
hasStencil
=
false
;
...
...
@@ -179,12 +203,13 @@ struct QMetalSwapChainData
id
<
MTLCommandBuffer
>
cb
[
QMTL_FRAMES_IN_FLIGHT
];
MTLRenderPassDescriptor
*
rp
=
nullptr
;
id
<
MTLTexture
>
msaaTex
[
QMTL_FRAMES_IN_FLIGHT
];
QRhiTexture
::
Format
rhiColorFormat
;
MTLPixelFormat
colorFormat
;
};
QRhiMetal
::
QRhiMetal
(
QRhiInitParams
*
params
)
{
d
=
new
QRhiMetalData
;
d
=
new
QRhiMetalData
(
this
)
;
QRhiMetalInitParams
*
metalparams
=
static_cast
<
QRhiMetalInitParams
*>
(
params
);
importedDevice
=
metalparams
->
importExistingDevice
;
...
...
@@ -220,6 +245,7 @@ void QRhiMetal::create()
void
QRhiMetal
::
destroy
()
{
executeDeferredReleases
(
true
);
finishActiveReadbacks
(
true
);
if
(
d
->
cmdQueue
)
{
[
d
->
cmdQueue
release
];
...
...
@@ -558,6 +584,7 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain)
return
QRhi
::
FrameOpSwapChainOutOfDate
;
}
currentSwapChain
=
swapChainD
;
currentFrameSlot
=
swapChainD
->
currentFrame
;
if
(
swapChainD
->
ds
)
swapChainD
->
ds
->
lastActiveFrameSlot
=
currentFrameSlot
;
...
...
@@ -568,7 +595,6 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain)
swapChainD
->
d
->
cb
[
currentFrameSlot
]
=
[
d
->
cmdQueue
commandBufferWithUnretainedReferences
];
swapChainD
->
cbWrapper
.
d
->
cb
=
swapChainD
->
d
->
cb
[
currentFrameSlot
];
swapChainD
->
cbWrapper
.
resetState
();
id
<
MTLTexture
>
scTex
=
swapChainD
->
d
->
curDrawable
.
texture
;
id
<
MTLTexture
>
resolveTex
=
nil
;
...
...
@@ -582,6 +608,8 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain)
swapChainD
->
rtWrapper
.
d
->
fb
.
hasStencil
=
swapChainD
->
ds
?
true
:
false
;
executeDeferredReleases
();
swapChainD
->
cbWrapper
.
resetState
();
finishActiveReadbacks
();
return
QRhi
::
FrameOpSuccess
;
}
...
...
@@ -603,6 +631,7 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain)
[
swapChainD
->
d
->
cb
[
currentFrameSlot
]
commit
];
swapChainD
->
currentFrame
=
(
swapChainD
->
currentFrame
+
1
)
%
QMTL_FRAMES_IN_FLIGHT
;
currentSwapChain
=
nullptr
;
++
finishedFrameCount
;
return
QRhi
::
FrameOpSuccess
;
...
...
@@ -610,13 +639,45 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain)
QRhi
::
FrameOpResult
QRhiMetal
::
beginOffscreenFrame
(
QRhiCommandBuffer
**
cb
)
{
Q_UNUSED
(
cb
);
return
QRhi
::
FrameOpError
;
Q_ASSERT
(
!
inFrame
);
inFrame
=
true
;
// Switch to the next slot manually. Swapchains do not know about this
// which is good. So for example a - unusual but possible - onscreen,
// onscreen, offscreen, onscreen, onscreen, onscreen sequence of
// begin/endFrame leads to 0, 1, 0, 0, 1, 0. This works because the
// offscreen frame is synchronous in the sense that we wait for execution
// to complete in endFrame, and so no resources used in that frame are busy
// anymore in the next frame.
currentFrameSlot
=
(
currentFrameSlot
+
1
)
%
QMTL_FRAMES_IN_FLIGHT
;
d
->
ofr
.
active
=
true
;
*
cb
=
&
d
->
ofr
.
cbWrapper
;
d
->
ofr
.
cbWrapper
.
d
->
cb
=
[
d
->
cmdQueue
commandBufferWithUnretainedReferences
];
executeDeferredReleases
();
d
->
ofr
.
cbWrapper
.
resetState
();
finishActiveReadbacks
();
return
QRhi
::
FrameOpSuccess
;
}
QRhi
::
FrameOpResult
QRhiMetal
::
endOffscreenFrame
()
{
return
QRhi
::
FrameOpError
;
Q_ASSERT
(
d
->
ofr
.
active
);
d
->
ofr
.
active
=
false
;
Q_ASSERT
(
inFrame
);
inFrame
=
false
;
[
d
->
ofr
.
cbWrapper
.
d
->
cb
commit
];
// offscreen frames wait for completion, unlike swapchain ones
[
d
->
ofr
.
cbWrapper
.
d
->
cb
waitUntilCompleted
];
finishActiveReadbacks
(
true
);
++
finishedFrameCount
;
return
QRhi
::
FrameOpSuccess
;
}
QRhi
::
FrameOpResult
QRhiMetal
::
finish
()
...
...
@@ -827,6 +888,60 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
destinationOrigin:
MTLOriginMake
(
dx
,
dy
,
0
)];
}
for
(
const
QRhiResourceUpdateBatchPrivate
::
TextureRead
&
u
:
ud
->
textureReadbacks
)
{
QRhiMetalData
::
ActiveReadback
aRb
;
aRb
.
activeFrameSlot
=
currentFrameSlot
;
aRb
.
desc
=
u
.
rb
;
aRb
.
result
=
u
.
result
;
QMetalTexture
*
texD
=
QRHI_RES
(
QMetalTexture
,
aRb
.
desc
.
texture
);
QMetalSwapChain
*
swapChainD
=
nullptr
;
id
<
MTLTexture
>
src
;
QSize
srcSize
;
if
(
texD
)
{
if
(
texD
->
samples
>
1
)
{
qWarning
(
"Multisample texture cannot be read back"
);
continue
;
}
aRb
.
pixelSize
=
texD
->
m_pixelSize
;
if
(
u
.
rb
.
level
>
0
)
{
aRb
.
pixelSize
.
setWidth
(
qFloor
(
float
(
qMax
(
1
,
aRb
.
pixelSize
.
width
()
>>
u
.
rb
.
level
))));
aRb
.
pixelSize
.
setHeight
(
qFloor
(
float
(
qMax
(
1
,
aRb
.
pixelSize
.
height
()
>>
u
.
rb
.
level
))));
}
aRb
.
format
=
texD
->
m_format
;
src
=
texD
->
d
->
tex
;
srcSize
=
texD
->
m_pixelSize
;
}
else
{
Q_ASSERT
(
currentSwapChain
);
swapChainD
=
QRHI_RES
(
QMetalSwapChain
,
currentSwapChain
);
aRb
.
pixelSize
=
swapChainD
->
pixelSize
;
aRb
.
format
=
swapChainD
->
d
->
rhiColorFormat
;
// Multisample swapchains need nothing special since resolving
// happens when ending a renderpass.
const
QMetalRenderTargetData
::
ColorAtt
&
colorAtt
(
swapChainD
->
rtWrapper
.
d
->
fb
.
colorAtt
[
0
]);
src
=
colorAtt
.
resolveTex
?
colorAtt
.
resolveTex
:
colorAtt
.
tex
;
srcSize
=
swapChainD
->
rtWrapper
.
d
->
pixelSize
;
}
quint32
bpl
=
0
;
textureFormatInfo
(
aRb
.
format
,
aRb
.
pixelSize
,
&
bpl
,
&
aRb
.
bufSize
);
aRb
.
buf
=
[
d
->
dev
newBufferWithLength
:
aRb
.
bufSize
options
:
MTLResourceStorageModeShared
];
ensureBlit
();
[
blitEnc
copyFromTexture
:
src
sourceSlice:
aRb
.
desc
.
layer
sourceLevel:
aRb
.
desc
.
level
sourceOrigin:
MTLOriginMake
(
0
,
0
,
0
)
sourceSize:
MTLSizeMake
(
srcSize
.
width
(),
srcSize
.
height
(),
1
)
toBuffer:
aRb
.
buf
destinationOffset:
0
destinationBytesPerRow:
bpl
destinationBytesPerImage:
0
options:
MTLBlitOptionNone
];
d
->
activeReadbacks
.
append
(
aRb
);
}
for
(
const
QRhiResourceUpdateBatchPrivate
::
TextureMipGen
&
u
:
ud
->
textureMipGens
)
{
ensureBlit
();
[
blitEnc
generateMipmapsForTexture
:
QRHI_RES
(
QMetalTexture
,
u
.
tex
)
->
d
->
tex
];
...
...
@@ -974,6 +1089,31 @@ void QRhiMetal::executeDeferredReleases(bool forced)
}
}
void
QRhiMetal
::
finishActiveReadbacks
(
bool
forced
)
{
QVarLengthArray
<
std
::
function
<
void
()
>
,
4
>
completedCallbacks
;
for
(
int
i
=
d
->
activeReadbacks
.
count
()
-
1
;
i
>=
0
;
--
i
)
{
const
QRhiMetalData
::
ActiveReadback
&
aRb
(
d
->
activeReadbacks
[
i
]);
if
(
forced
||
currentFrameSlot
==
aRb
.
activeFrameSlot
||
aRb
.
activeFrameSlot
<
0
)
{
aRb
.
result
->
format
=
aRb
.
format
;
aRb
.
result
->
pixelSize
=
aRb
.
pixelSize
;
aRb
.
result
->
data
.
resize
(
aRb
.
bufSize
);
void
*
p
=
[
aRb
.
buf
contents
];
memcpy
(
aRb
.
result
->
data
.
data
(),
p
,
aRb
.
bufSize
);
[
aRb
.
buf
release
];
if
(
aRb
.
result
->
completed
)
completedCallbacks
.
append
(
aRb
.
result
->
completed
);
d
->
activeReadbacks
.
removeAt
(
i
);
}
}
for
(
auto
f
:
completedCallbacks
)
f
();
}
QMetalBuffer
::
QMetalBuffer
(
QRhiImplementation
*
rhi
,
Type
type
,
UsageFlags
usage
,
int
size
)
:
QRhiBuffer
(
rhi
,
type
,
usage
,
size
),
d
(
new
QMetalBufferData
)
...
...
@@ -2116,6 +2256,7 @@ void QMetalSwapChain::chooseFormats()
samples
=
rhiD
->
effectiveSampleCount
(
m_sampleCount
);
// pick a format that is allowed for CAMetalLayer.pixelFormat
d
->
colorFormat
=
m_flags
.
testFlag
(
sRGB
)
?
MTLPixelFormatBGRA8Unorm_sRGB
:
MTLPixelFormatBGRA8Unorm
;
d
->
rhiColorFormat
=
QRhiTexture
::
BGRA8
;
}
bool
QMetalSwapChain
::
buildOrResize
()
...
...
@@ -2136,6 +2277,9 @@ bool QMetalSwapChain::buildOrResize()
if
(
d
->
colorFormat
!=
d
->
layer
.
pixelFormat
)
d
->
layer
.
pixelFormat
=
d
->
colorFormat
;
if
(
m_flags
.
testFlag
(
UsedAsTransferSource
))
d
->
layer
.
framebufferOnly
=
NO
;
m_currentPixelSize
=
surfacePixelSize
();
pixelSize
=
m_currentPixelSize
;
...
...
src/rhi/qrhimetal_p.h
View file @
b4cfad8c
...
...
@@ -315,6 +315,7 @@ public:
void
create
();
void
destroy
();
void
executeDeferredReleases
(
bool
forced
=
false
);
void
finishActiveReadbacks
(
bool
forced
=
false
);
void
enqueueResourceUpdates
(
QRhiCommandBuffer
*
cb
,
QRhiResourceUpdateBatch
*
resourceUpdates
);
void
executeBufferHostWritesForCurrentFrame
(
QMetalBuffer
*
bufD
);
int
effectiveSampleCount
(
int
sampleCount
)
const
;
...
...
@@ -324,6 +325,7 @@ public:
int
currentFrameSlot
=
0
;
int
finishedFrameCount
=
0
;
bool
inPass
=
false
;
QMetalSwapChain
*
currentSwapChain
=
nullptr
;
QRhiMetalData
*
d
=
nullptr
;
};
...
...
src/rhi/qrhivulkan.cpp
View file @
b4cfad8c
...
...
@@ -940,7 +940,7 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
VkImageUsageFlags
usage
=
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
;
swapChainD
->
supportsReadback
=
(
surfaceCaps
.
supportedUsageFlags
&
VK_IMAGE_USAGE_TRANSFER_SRC_BIT
);
if
(
swapChainD
->
supportsReadback
)
if
(
swapChainD
->
supportsReadback
&&
swapChainD
->
m_flags
.
testFlag
(
QRhiSwapChain
::
UsedAsTransferSource
)
)
usage
|=
VK_IMAGE_USAGE_TRANSFER_SRC_BIT
;
qDebug
(
"Creating new swapchain of %d buffers, size %dx%d"
,
...
...
todo.txt
View file @
b4cfad8c
mtl: rhi without a window, offscreen frame