Merge remote-tracking branch 'origin/5.14' into 5.15

Change-Id: I43393cf47675fd6c14972df1221986335c6f493c
bb10
Qt Forward Merge Bot 2019-10-04 01:01:47 +02:00
commit 3a1613c227
47 changed files with 788 additions and 926 deletions

View File

@ -818,6 +818,7 @@
"rpath": {
"label": "Build with RPATH",
"autoDetect": "var.QMAKE_LFLAGS_RPATH != '' && features.shared",
"condition": "!config.android",
"output": [ "publicFeature", "publicQtConfig" ]
},
"rpath_dir": {

View File

@ -62,7 +62,7 @@ FlowLayout::FlowLayout(QGraphicsLayoutItem *parent) : QGraphicsLayout(parent)
void FlowLayout::insertItem(int index, QGraphicsLayoutItem *item)
{
item->setParentLayoutItem(this);
if (index > m_items.count())
if (index > m_items.count() || index < 0)
index = m_items.count();
m_items.insert(index, item);
invalidate();

View File

@ -55,7 +55,7 @@ INSTALLS += target
# Some final setup
TARGET = $$qt5LibraryTarget($$TARGET)
TARGET = $$qt5LibraryTarget($$TARGET, "qml/$$TARGETPATH/")
load(qt_targets)
load(qt_common)

View File

@ -21,7 +21,13 @@ defineReplace(qtLibraryTarget) {
}
defineReplace(qt5LibraryTarget) {
LIBRARY_NAME = $$qtLibraryTarget($$1)
android {
LIBRARY_NAME_PREFIX = $$2
LIBRARY_NAME_PREFIX = $$replace(LIBRARY_NAME_PREFIX, "//", "/")
LIBRARY_NAME_PREFIX = $$replace(LIBRARY_NAME_PREFIX, "/", "_")
LIBRARY_NAME = $$LIBRARY_NAME_PREFIX$$qtLibraryTarget($$1)
unset(LIBRARY_NAME_PREFIX)
} else: LIBRARY_NAME = $$qtLibraryTarget($$1)
isEmpty(QMAKE_FRAMEWORK_BUNDLE_NAME) {
# Insert the major version of Qt in the library name
# unless it's a framework build.

View File

@ -92,7 +92,7 @@ target.path = $$[QT_INSTALL_PLUGINS]/$$PLUGIN_TYPE
INSTALLS += target
qt_libinfix_plugins: TARGET = $$TARGET$$QT_LIBINFIX
TARGET = $$qt5LibraryTarget($$TARGET)
TARGET = $$qt5LibraryTarget($$TARGET, "plugins/$$PLUGIN_TYPE/")
CONFIG += create_cmake

View File

@ -448,15 +448,21 @@ UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
opt.remove(suffixMarker); // Apply suffix by removing marker
}
for (const QMakeLocalFileName &dir : qAsConst(frameworkdirs)) {
auto processPrlIfFound = [&](QString directory) {
QString suffixedPrl = directory + opt;
if (processPrlFile(suffixedPrl, true))
return true;
if (hasSuffix) {
QString unsuffixedPrl = directory + frameworkName;
if (processPrlFile(unsuffixedPrl, true))
return true;
}
return false;
};
QString frameworkDirectory = dir.local() + "/" + frameworkName + + ".framework/";
QString suffixedPrl = frameworkDirectory + opt;
if (processPrlFile(suffixedPrl, true))
if (processPrlIfFound(frameworkDirectory + "Resources/")
|| processPrlIfFound(frameworkDirectory))
break;
if (hasSuffix) {
QString unsuffixedPrl = frameworkDirectory + frameworkName;
if (processPrlFile(unsuffixedPrl, true))
break;
}
}
} else {
if (opt.length() == 10)

View File

@ -1230,8 +1230,9 @@ void UnixMakefileGenerator::init2()
else
ar_cmd.append("$(AR) $(TARGETA) $(OBJECTS)");
if (!project->isEmpty("QMAKE_BUNDLE")) {
project->values("PRL_TARGET").prepend(
project->first("QMAKE_BUNDLE") + Option::dir_sep + project->first("TARGET"));
project->values("PRL_TARGET").prepend(project->first("QMAKE_BUNDLE") +
"/Versions/" + project->first("QMAKE_FRAMEWORK_VERSION") +
"/Resources/" + project->first("TARGET"));
ProString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION");
if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/"))
bundle_loc.prepend("/");

View File

@ -1013,6 +1013,25 @@ public class QtNative
});
}
private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
String [] list;
ArrayList<String> res = new ArrayList<String>();
try {
list = asset.list(path);
if (list.length > 0) {
for (String file : list) {
try {
String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file);
if (isDir != null && isDir.length > 0)
file += "/";
res.add(file);
} catch (Exception e) {}
}
}
} catch (Exception e) {}
return res.toArray(new String[res.size()]);
}
// screen methods
public static native void setDisplayMetrics(int screenWidthPixels,
int screenHeightPixels,

View File

@ -44,8 +44,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.res.AssetManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@ -55,15 +53,8 @@ import android.util.Log;
import org.kde.necessitas.ministro.IMinistro;
import org.kde.necessitas.ministro.IMinistroCallback;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@ -88,8 +79,6 @@ public abstract class QtLoader {
public static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
public static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
public static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
public static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id";
public static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id";
public static final String MAIN_LIBRARY_KEY = "main.library";
public static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
public static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level";
@ -141,7 +130,6 @@ public abstract class QtLoader {
public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme.
public static final int INCOMPATIBLE_MINISTRO_VERSION = 1; // Incompatible Ministro version. Ministro needs to be upgraded.
public static final int BUFFER_SIZE = 1024;
public String[] m_sources = {"https://download.qt-project.org/ministro/android/qt5/qt-5.7"}; // Make sure you are using ONLY secure locations
public String m_repository = "default"; // Overwrites the default Ministro repository
@ -368,263 +356,6 @@ public abstract class QtLoader {
errorDialog.show();
}
static private void copyFile(InputStream inputStream, OutputStream outputStream)
throws IOException
{
byte[] buffer = new byte[BUFFER_SIZE];
int count;
while ((count = inputStream.read(buffer)) > 0)
outputStream.write(buffer, 0, count);
}
private void copyAsset(String source, String destination)
throws IOException
{
// Already exists, we don't have to do anything
File destinationFile = new File(destination);
if (destinationFile.exists())
return;
File parentDirectory = destinationFile.getParentFile();
if (!parentDirectory.exists())
parentDirectory.mkdirs();
destinationFile.createNewFile();
AssetManager assetsManager = m_context.getAssets();
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = assetsManager.open(source);
outputStream = new FileOutputStream(destinationFile);
copyFile(inputStream, outputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null)
inputStream.close();
if (outputStream != null)
// Ensure that the buffered data is flushed to the OS for writing.
outputStream.flush();
}
// Mark the output stream as still needing to be written to physical disk.
// The output stream will be closed after this sync completes.
m_fileOutputStreams.add(outputStream);
}
private static void createBundledBinary(String source, String destination)
throws IOException
{
// Already exists, we don't have to do anything
File destinationFile = new File(destination);
if (destinationFile.exists())
return;
File parentDirectory = destinationFile.getParentFile();
if (!parentDirectory.exists())
parentDirectory.mkdirs();
destinationFile.createNewFile();
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new FileOutputStream(destinationFile);
copyFile(inputStream, outputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null)
inputStream.close();
if (outputStream != null)
// Ensure that the buffered data is flushed to the OS for writing.
outputStream.flush();
}
// Mark the output stream as still needing to be written to physical disk.
// The output stream will be closed after this sync completes.
m_fileOutputStreams.add(outputStream);
}
private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion)
{
File versionFile = new File(pluginsPrefix + "cache.version");
long cacheVersion = 0;
if (versionFile.exists() && versionFile.canRead()) {
DataInputStream inputStream = null;
try {
inputStream = new DataInputStream(new FileInputStream(versionFile));
cacheVersion = inputStream.readLong();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
if (cacheVersion != packageVersion) {
deleteRecursively(new File(pluginsPrefix));
return true;
} else {
return false;
}
}
private void extractBundledPluginsAndImports(String pluginsPrefix, String libsDir)
throws IOException
{
long packageVersion = -1;
try {
PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.getPackageName(), 0);
packageVersion = packageInfo.lastUpdateTime;
} catch (Exception e) {
e.printStackTrace();
}
if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion))
return;
{
// why can't we load the plugins directly from libs ?!?!
String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
if (m_contextInfo.metaData.containsKey(key)) {
int resourceId = m_contextInfo.metaData.getInt(key);
ArrayList<String> list = prefferedAbiLibs(m_context.getResources().getStringArray(resourceId));
for (String bundledImportBinary : list) {
String[] split = bundledImportBinary.split(":");
String sourceFileName = libsDir + split[0];
String destinationFileName = pluginsPrefix + split[1];
createBundledBinary(sourceFileName, destinationFileName);
}
}
}
{
String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY;
if (m_contextInfo.metaData.containsKey(key)) {
String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
for (String fileName : list) {
String[] split = fileName.split(":");
String sourceFileName = split[0];
String destinationFileName = pluginsPrefix + split[1];
copyAsset(sourceFileName, destinationFileName);
}
}
}
// The Java compiler must be assured that variables belonging to this parent thread will not
// go out of scope during the runtime of the spawned thread (since in general spawned
// threads can outlive their parent threads). Copy variables and declare as 'final' before
// passing into the spawned thread.
final String pluginsPrefixFinal = pluginsPrefix;
final long packageVersionFinal = packageVersion;
// Spawn a worker thread to write all installed files to physical disk and indicate
// successful installation by creating the 'cache.version' file.
new Thread(new Runnable() {
@Override
public void run() {
try {
finalizeInstallation(pluginsPrefixFinal, packageVersionFinal);
} catch (Exception e) {
Log.e(QtApplication.QtTAG, e.getMessage());
e.printStackTrace();
return;
}
}
}).start();
}
private void finalizeInstallation(String pluginsPrefix, long packageVersion)
throws IOException
{
{
// Write all installed files to physical disk and close each output stream
for (FileOutputStream fileOutputStream : m_fileOutputStreams) {
fileOutputStream.getFD().sync();
fileOutputStream.close();
}
m_fileOutputStreams.clear();
}
{
// Create 'cache.version' file
File versionFile = new File(pluginsPrefix + "cache.version");
File parentDirectory = versionFile.getParentFile();
if (!parentDirectory.exists())
parentDirectory.mkdirs();
versionFile.createNewFile();
DataOutputStream outputStream = null;
try {
outputStream = new DataOutputStream(new FileOutputStream(versionFile));
outputStream.writeLong(packageVersion);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null)
outputStream.close();
}
}
}
private void deleteRecursively(File directory)
{
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory())
deleteRecursively(file);
else
file.delete();
}
directory.delete();
}
}
private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix)
{
File newCache = new File(localPrefix);
if (!newCache.exists()) {
{
File oldPluginsCache = new File(oldLocalPrefix + "plugins/");
if (oldPluginsCache.exists() && oldPluginsCache.isDirectory())
deleteRecursively(oldPluginsCache);
}
{
File oldImportsCache = new File(oldLocalPrefix + "imports/");
if (oldImportsCache.exists() && oldImportsCache.isDirectory())
deleteRecursively(oldImportsCache);
}
{
File oldQmlCache = new File(oldLocalPrefix + "qml/");
if (oldQmlCache.exists() && oldQmlCache.isDirectory())
deleteRecursively(oldQmlCache);
}
}
}
public void startApp(final boolean firstStart)
{
try {
@ -688,29 +419,13 @@ public abstract class QtLoader {
if (m_contextInfo.metaData.containsKey("android.app.bundle_local_qt_libs")
&& m_contextInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) {
File dataDir = new File(m_context.getApplicationInfo().dataDir);
String dataPath = dataDir.getCanonicalPath() + "/";
String pluginsPrefix = dataPath + "qt-reserved-files/";
if (libsDir == null)
throw new Exception("Invalid libsDir");
cleanOldCacheIfNecessary(dataPath, pluginsPrefix);
extractBundledPluginsAndImports(pluginsPrefix, libsDir);
if (m_contextInfo.metaData.containsKey(BUNDLED_IN_LIB_RESOURCE_ID_KEY)) {
int resourceId = m_contextInfo.metaData.getInt("android.app.load_local_libs_resource_id");
for (String libs : prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))) {
for (String lib : libs.split(":")) {
if (!lib.isEmpty())
libraryList.add(libsDir + lib);
}
int resourceId = m_contextInfo.metaData.getInt("android.app.load_local_libs_resource_id");
for (String libs : prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))) {
for (String lib : libs.split(":")) {
if (!lib.isEmpty())
libraryList.add(libsDir + lib);
}
}
ENVIRONMENT_VARIABLES += "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml"
+ "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports"
+ "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins";
if (bundledLibsDir != null)
ENVIRONMENT_VARIABLES += "\tQT_BUNDLED_LIBS_PATH=" + bundledLibsDir;
}

View File

@ -34,8 +34,6 @@
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>

View File

@ -54,4 +54,9 @@ android {
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
}

View File

@ -11,20 +11,12 @@
<!-- %%INSERT_EXTRA_LIBS%% -->
</array>
<array name="qt_libs">
<array name="qt_libs">
<!-- %%INSERT_QT_LIBS%% -->
</array>
<array name="bundled_in_lib">
<!-- %%INSERT_BUNDLED_IN_LIB%% -->
</array>
<array name="load_local_libs">
<!-- %%INSERT_LOCAL_LIBS%% -->
</array>
<array name="bundled_in_assets">
<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
</array>
</resources>

View File

@ -21,7 +21,7 @@ CONFIG += simd optimize_full
QMAKE_DOCS = $$PWD/doc/qtcore.qdocconf
ANDROID_LIB_DEPENDENCIES = \
plugins/platforms/libqtforandroid.so
plugins/platforms/libplugins_platforms_qtforandroid.so
ANDROID_BUNDLED_JAR_DEPENDENCIES = \
jar/QtAndroid.jar
ANDROID_PERMISSIONS = \

View File

@ -56,6 +56,14 @@
# include <UIKit/UIApplication.h>
#endif
QT_BEGIN_NAMESPACE
namespace QtPrivate {
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher");
Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
}
using namespace QtPrivate;
QT_END_NAMESPACE
QT_USE_NAMESPACE
/*
@ -148,9 +156,6 @@ static CFStringRef runLoopMode(NSDictionary *dictionary)
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher");
Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
class RunLoopDebugger : public QObject
{
Q_OBJECT

View File

@ -98,8 +98,10 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(RunLoopModeTracker));
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher);
Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers)
namespace QtPrivate {
Q_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher);
Q_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers)
}
class QEventDispatcherCoreFoundation;

View File

@ -209,7 +209,7 @@ void QFactoryLoader::update()
#if defined(Q_OS_WIN)
QStringList(QStringLiteral("*.dll")),
#elif defined(Q_OS_ANDROID)
QStringList(QLatin1String("plugins_%1_*.so").arg(d->suffix)),
QStringList(QLatin1String("libplugins_%1_*.so").arg(d->suffix)),
#endif
QDir::Files);
QLibraryPrivate *library = 0;

View File

@ -791,37 +791,44 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta
\obsolete
This constructor has been deprecated.
*/
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
: QWheelEvent(pos, globalPos, pixelDelta, angleDelta, qt4Delta, qt4Orientation,
buttons, modifiers, Qt::NoScrollPhase)
{}
QT_WARNING_POP
/*!
\obsolete
This constructor has been deprecated.
*/
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase)
: QWheelEvent(pos, globalPos, pixelDelta, angleDelta, qt4Delta, qt4Orientation,
buttons, modifiers, phase, Qt::MouseEventNotSynthesized)
{}
QT_WARNING_POP
/*!
\obsolete
This constructor has been deprecated.
*/
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source)
: QWheelEvent(pos, globalPos, pixelDelta, angleDelta, qt4Delta, qt4Orientation,
buttons, modifiers, phase, source, false)
{}
QT_WARNING_POP
/*!
\obsolete
@ -3930,12 +3937,15 @@ QDebug operator<<(QDebug dbg, const QEvent *e)
case QEvent::Wheel: {
const QWheelEvent *we = static_cast<const QWheelEvent *>(e);
dbg << "QWheelEvent(" << we->phase();
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED // delta() and orientation()
if (!we->pixelDelta().isNull() || !we->angleDelta().isNull())
dbg << ", pixelDelta=" << we->pixelDelta() << ", angleDelta=" << we->angleDelta();
#if QT_DEPRECATED_SINCE(5, 14)
else if (int qt4Delta = we->delta())
dbg << ", delta=" << qt4Delta << ", orientation=" << we->orientation();
#endif
QT_WARNING_POP
dbg << ')';
}
break;

View File

@ -2247,8 +2247,11 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh
}
#if QT_DEPRECATED_SINCE(5, 14)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation,
mouse_buttons, e->modifiers, e->phase, e->source, e->inverted);
QT_WARNING_POP
#else
QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta,
mouse_buttons, e->modifiers, e->phase, e->inverted, e->source);

View File

@ -1474,6 +1474,8 @@ void QOpenGL2PaintEngineEx::renderHintsChanged()
#ifndef QT_OPENGL_ES_2
if (!QOpenGLContext::currentContext()->isOpenGLES()) {
Q_D(QOpenGL2PaintEngineEx);
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if ((state()->renderHints & QPainter::Antialiasing)
#if QT_DEPRECATED_SINCE(5, 14)
|| (state()->renderHints & QPainter::HighQualityAntialiasing)
@ -1482,6 +1484,7 @@ void QOpenGL2PaintEngineEx::renderHintsChanged()
d->funcs.glEnable(GL_MULTISAMPLE);
else
d->funcs.glDisable(GL_MULTISAMPLE);
QT_WARNING_POP
}
#endif // QT_OPENGL_ES_2

View File

@ -906,8 +906,11 @@ void QRasterPaintEngine::renderHintsChanged()
s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
#if QT_DEPRECATED_SINCE(5, 14)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if (s->renderHints & QPainter::HighQualityAntialiasing)
s->flags.antialiased = true;
QT_WARNING_POP
#endif
s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);

View File

@ -563,7 +563,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value BaseInstance Indicates that instanced draw commands support the \c
firstInstance argument. When reported as not supported, the firstInstance
value is ignored and the instance ID starts from 0.
*/
\value TriangleFanTopology Indicates that QRhiGraphicsPipeline::setTopology()
supports QRhiGraphicsPipeline::TriangleFan.
*/
/*!
\enum QRhi::BeginFrameFlag
@ -1199,8 +1202,7 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
*/
bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW
{
return a.bindings() == b.bindings()
&& a.attributes() == b.attributes();
return a.m_bindings == b.m_bindings && a.m_attributes == b.m_attributes;
}
/*!
@ -1221,15 +1223,21 @@ bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b)
*/
uint qHash(const QRhiVertexInputLayout &v, uint seed) Q_DECL_NOTHROW
{
return qHash(v.bindings(), seed) + qHash(v.attributes(), seed);
return qHash(v.m_bindings, seed) + qHash(v.m_attributes, seed);
}
#ifndef QT_NO_DEBUG_STREAM
template<typename T, int N>
QDebug operator<<(QDebug dbg, const QVarLengthArray<T, N> &vla)
{
return QtPrivate::printSequentialContainer(dbg, "VLA", vla);
}
QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "QRhiVertexInputLayout(bindings=" << v.bindings()
<< " attributes=" << v.attributes()
dbg.nospace() << "QRhiVertexInputLayout(bindings=" << v.m_bindings
<< " attributes=" << v.m_attributes
<< ')';
return dbg;
}
@ -1630,27 +1638,19 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QRhiTextureUplo
}
/*!
Constructs a texture upload description with the specified list of \a entries.
Constructs a texture upload description with the specified \a list of entries.
\note \a entries can also contain multiple QRhiTextureUploadEntry elements
\note \a list can also contain multiple QRhiTextureUploadEntry elements
with the the same layer and level. This makes sense when those uploads are
partial, meaning their subresource description has a source size or image
smaller than the subresource dimensions, and can be more efficient than
issuing separate uploadTexture()'s.
*/
QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries)
: m_entries(entries)
QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list<QRhiTextureUploadEntry> list)
: m_entries(list)
{
}
/*!
Adds \a entry to the list of subresource uploads.
*/
void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
{
m_entries.append(entry);
}
/*!
\class QRhiTextureCopyDescription
\internal
@ -3056,11 +3056,6 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
#endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVarLengthArray<QRhiShaderResourceBinding, 8> &bindings)
{
return QtPrivate::printSequentialContainer(dbg, "Bindings", bindings);
}
QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
{
QDebugStateSaver saver(dbg);
@ -3131,6 +3126,7 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
\value Triangles (default)
\value TriangleStrip
\value TriangleFan (only available if QRhi::TriangleFanTopology is supported)
\value Lines
\value LineStrip
\value Points
@ -4215,7 +4211,7 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da
*/
void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
{
if (!desc.entries().isEmpty())
if (desc.cbeginEntries() != desc.cendEntries())
d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureUpload(tex, desc));
}

View File

@ -240,15 +240,42 @@ class Q_GUI_EXPORT QRhiVertexInputLayout
public:
QRhiVertexInputLayout() = default;
QVector<QRhiVertexInputBinding> bindings() const { return m_bindings; }
void setBindings(const QVector<QRhiVertexInputBinding> &v) { m_bindings = v; }
void setBindings(std::initializer_list<QRhiVertexInputBinding> list) { m_bindings = list; }
template<typename InputIterator>
void setBindings(InputIterator first, InputIterator last)
{
m_bindings.clear();
std::copy(first, last, std::back_inserter(m_bindings));
}
void setBindings(const QVector<QRhiVertexInputBinding> &bindings) // compat., to be removed
{
setBindings(bindings.cbegin(), bindings.cend());
}
const QRhiVertexInputBinding *cbeginBindings() const { return m_bindings.cbegin(); }
const QRhiVertexInputBinding *cendBindings() const { return m_bindings.cend(); }
const QRhiVertexInputBinding *bindingAt(int index) const { return &m_bindings.at(index); }
QVector<QRhiVertexInputAttribute> attributes() const { return m_attributes; }
void setAttributes(const QVector<QRhiVertexInputAttribute> &v) { m_attributes = v; }
void setAttributes(std::initializer_list<QRhiVertexInputAttribute> list) { m_attributes = list; }
template<typename InputIterator>
void setAttributes(InputIterator first, InputIterator last)
{
m_attributes.clear();
std::copy(first, last, std::back_inserter(m_attributes));
}
void setAttributes(const QVector<QRhiVertexInputAttribute> &attributes) // compat., to be removed
{
setAttributes(attributes.cbegin(), attributes.cend());
}
const QRhiVertexInputAttribute *cbeginAttributes() const { return m_attributes.cbegin(); }
const QRhiVertexInputAttribute *cendAttributes() const { return m_attributes.cend(); }
private:
QVector<QRhiVertexInputBinding> m_bindings;
QVector<QRhiVertexInputAttribute> m_attributes;
QVarLengthArray<QRhiVertexInputBinding, 8> m_bindings;
QVarLengthArray<QRhiVertexInputAttribute, 8> m_attributes;
friend Q_GUI_EXPORT bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW;
friend Q_GUI_EXPORT uint qHash(const QRhiVertexInputLayout &v, uint seed) Q_DECL_NOTHROW;
friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
};
Q_DECLARE_TYPEINFO(QRhiVertexInputLayout, Q_MOVABLE_TYPE);
@ -439,8 +466,16 @@ public:
QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiRenderBuffer *depthStencilBuffer);
QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiTexture *depthTexture);
QVector<QRhiColorAttachment> colorAttachments() const { return m_colorAttachments; }
void setColorAttachments(const QVector<QRhiColorAttachment> &att) { m_colorAttachments = att; }
void setColorAttachments(std::initializer_list<QRhiColorAttachment> list) { m_colorAttachments = list; }
template<typename InputIterator>
void setColorAttachments(InputIterator first, InputIterator last)
{
m_colorAttachments.clear();
std::copy(first, last, std::back_inserter(m_colorAttachments));
}
const QRhiColorAttachment *cbeginColorAttachments() const { return m_colorAttachments.cbegin(); }
const QRhiColorAttachment *cendColorAttachments() const { return m_colorAttachments.cend(); }
const QRhiColorAttachment *colorAttachmentAt(int index) const { return &m_colorAttachments.at(index); }
QRhiRenderBuffer *depthStencilBuffer() const { return m_depthStencilBuffer; }
void setDepthStencilBuffer(QRhiRenderBuffer *renderBuffer) { m_depthStencilBuffer = renderBuffer; }
@ -449,7 +484,7 @@ public:
void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; }
private:
QVector<QRhiColorAttachment> m_colorAttachments;
QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
QRhiTexture *m_depthTexture = nullptr;
};
@ -516,14 +551,23 @@ class Q_GUI_EXPORT QRhiTextureUploadDescription
public:
QRhiTextureUploadDescription() = default;
QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry);
QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries);
QRhiTextureUploadDescription(std::initializer_list<QRhiTextureUploadEntry> list);
QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries) // compat., to be removed
: m_entries(entries.cbegin(), entries.cend())
{ }
QVector<QRhiTextureUploadEntry> entries() const { return m_entries; }
void setEntries(const QVector<QRhiTextureUploadEntry> &entries) { m_entries = entries; }
void append(const QRhiTextureUploadEntry &entry);
void setEntries(std::initializer_list<QRhiTextureUploadEntry> list) { m_entries = list; }
template<typename InputIterator>
void setEntries(InputIterator first, InputIterator last)
{
m_entries.clear();
std::copy(first, last, std::back_inserter(m_entries));
}
const QRhiTextureUploadEntry *cbeginEntries() const { return m_entries.cbegin(); }
const QRhiTextureUploadEntry *cendEntries() const { return m_entries.cend(); }
private:
QVector<QRhiTextureUploadEntry> m_entries;
QVarLengthArray<QRhiTextureUploadEntry, 16> m_entries;
};
Q_DECLARE_TYPEINFO(QRhiTextureUploadDescription, Q_MOVABLE_TYPE);
@ -973,6 +1017,7 @@ public:
enum Topology {
Triangles,
TriangleStrip,
TriangleFan,
Lines,
LineStrip,
Points
@ -1382,7 +1427,8 @@ public:
WideLines,
VertexShaderPointSize,
BaseVertex,
BaseInstance
BaseInstance,
TriangleFanTopology
};
enum BeginFrameFlag {

View File

@ -328,9 +328,8 @@ public:
TextureOp op;
op.type = Upload;
op.upload.tex = tex;
const QVector<QRhiTextureUploadEntry> &entries(desc.entries());
for (const QRhiTextureUploadEntry &entry : entries)
op.upload.subresDesc[entry.layer()][entry.level()].append(entry.description());
for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
op.upload.subresDesc[it->layer()][it->level()].append(it->description());
return op;
}

View File

@ -469,6 +469,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::BaseInstance:
return true;
case QRhi::TriangleFanTopology:
return false;
default:
Q_UNREACHABLE();
return false;
@ -742,13 +744,14 @@ void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb,
cmd.cmd = QD3D11CommandBuffer::Command::BindVertexBuffers;
cmd.args.bindVertexBuffers.startSlot = startBinding;
cmd.args.bindVertexBuffers.slotCount = bindingCount;
const QVector<QRhiVertexInputBinding> inputBindings =
QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline)->m_vertexInputLayout.bindings();
for (int i = 0, ie = qMin(bindingCount, inputBindings.count()); i != ie; ++i) {
QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
const QRhiVertexInputLayout &inputLayout(psD->m_vertexInputLayout);
const int inputBindingCount = inputLayout.cendBindings() - inputLayout.cbeginBindings();
for (int i = 0, ie = qMin(bindingCount, inputBindingCount); i != ie; ++i) {
QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer;
cmd.args.bindVertexBuffers.offsets[i] = bindings[i].second;
cmd.args.bindVertexBuffers.strides[i] = inputBindings[i].stride();
cmd.args.bindVertexBuffers.strides[i] = inputLayout.bindingAt(i)->stride();
}
cbD->commands.append(cmd);
}
@ -1606,9 +1609,10 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget);
const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
for (int att = 0, attCount = colorAttachments.count(); att != attCount; ++att) {
const QRhiColorAttachment &colorAtt(colorAttachments[att]);
for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
it != itEnd; ++it)
{
const QRhiColorAttachment &colorAtt(*it);
if (!colorAtt.resolveTexture())
continue;
@ -2953,17 +2957,20 @@ bool QD3D11TextureRenderTarget::build()
if (rtv[0] || dsv)
release();
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
QRHI_RES_RHI(QRhiD3D11);
d.colorAttCount = colorAttachments.count();
for (int i = 0; i < d.colorAttCount; ++i) {
QRhiTexture *texture = colorAttachments[i].texture();
QRhiRenderBuffer *rb = colorAttachments[i].renderBuffer();
d.colorAttCount = 0;
int attIndex = 0;
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
d.colorAttCount += 1;
const QRhiColorAttachment &colorAtt(*it);
QRhiTexture *texture = colorAtt.texture();
QRhiRenderBuffer *rb = colorAtt.renderBuffer();
Q_ASSERT(texture || rb);
if (texture) {
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
@ -2972,32 +2979,32 @@ bool QD3D11TextureRenderTarget::build()
rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.MipSlice = UINT(colorAttachments[i].level());
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAttachments[i].layer());
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
rtvDesc.Texture2DArray.ArraySize = 1;
} else {
if (texD->sampleDesc.Count > 1) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
} else {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Texture2D.MipSlice = UINT(colorAttachments[i].level());
rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
}
}
HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[i]);
HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[attIndex]);
if (FAILED(hr)) {
qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
return false;
}
ownsRtv[i] = true;
if (i == 0) {
ownsRtv[attIndex] = true;
if (attIndex == 0) {
d.pixelSize = texD->pixelSize();
d.sampleCount = int(texD->sampleDesc.Count);
}
} else if (rb) {
QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb);
ownsRtv[i] = false;
rtv[i] = rbD->rtv;
if (i == 0) {
ownsRtv[attIndex] = false;
rtv[attIndex] = rbD->rtv;
if (attIndex == 0) {
d.pixelSize = rbD->pixelSize();
d.sampleCount = int(rbD->sampleDesc.Count);
}
@ -3542,22 +3549,22 @@ bool QD3D11GraphicsPipeline::build()
d3dTopology = toD3DTopology(m_topology);
if (!vsByteCode.isEmpty()) {
const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
QVarLengthArray<D3D11_INPUT_ELEMENT_DESC, 4> inputDescs;
for (const QRhiVertexInputAttribute &attribute : attributes) {
for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
it != itEnd; ++it)
{
D3D11_INPUT_ELEMENT_DESC desc;
memset(&desc, 0, sizeof(desc));
// the output from SPIRV-Cross uses TEXCOORD<location> as the semantic
desc.SemanticName = "TEXCOORD";
desc.SemanticIndex = UINT(attribute.location());
desc.Format = toD3DAttributeFormat(attribute.format());
desc.InputSlot = UINT(attribute.binding());
desc.AlignedByteOffset = attribute.offset();
const QRhiVertexInputBinding &binding(bindings[attribute.binding()]);
if (binding.classification() == QRhiVertexInputBinding::PerInstance) {
desc.SemanticIndex = UINT(it->location());
desc.Format = toD3DAttributeFormat(it->format());
desc.InputSlot = UINT(it->binding());
desc.AlignedByteOffset = it->offset();
const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
desc.InstanceDataStepRate = UINT(binding.instanceStepRate());
desc.InstanceDataStepRate = UINT(inputBinding->instanceStepRate());
} else {
desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
}

View File

@ -732,6 +732,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return caps.baseVertex;
case QRhi::BaseInstance:
return false; // not in ES 3.2, so won't bother
case QRhi::TriangleFanTopology:
return true;
default:
Q_UNREACHABLE();
return false;
@ -1539,6 +1541,8 @@ static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
return GL_TRIANGLES;
case QRhiGraphicsPipeline::TriangleStrip:
return GL_TRIANGLE_STRIP;
case QRhiGraphicsPipeline::TriangleFan:
return GL_TRIANGLE_FAN;
case QRhiGraphicsPipeline::Lines:
return GL_LINES;
case QRhiGraphicsPipeline::LineStrip:
@ -1898,21 +1902,22 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
{
QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
if (psD) {
const QVector<QRhiVertexInputBinding> bindings = psD->m_vertexInputLayout.bindings();
const QVector<QRhiVertexInputAttribute> attributes = psD->m_vertexInputLayout.attributes();
for (const QRhiVertexInputAttribute &a : attributes) {
const int bindingIdx = a.binding();
for (auto it = psD->m_vertexInputLayout.cbeginAttributes(), itEnd = psD->m_vertexInputLayout.cendAttributes();
it != itEnd; ++it)
{
const int bindingIdx = it->binding();
if (bindingIdx != cmd.args.bindVertexBuffer.binding)
continue;
// we do not support more than one vertex buffer
f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer);
const int stride = int(bindings[bindingIdx].stride());
const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(bindingIdx);
const int stride = int(inputBinding->stride());
int size = 1;
GLenum type = GL_FLOAT;
bool normalize = false;
switch (a.format()) {
switch (it->format()) {
case QRhiVertexInputAttribute::Float4:
type = GL_FLOAT;
size = 4;
@ -1948,16 +1953,13 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
}
const int locationIdx = a.location();
quint32 ofs = a.offset() + cmd.args.bindVertexBuffer.offset;
const int locationIdx = it->location();
quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset;
f->glVertexAttribPointer(GLuint(locationIdx), size, type, normalize, stride,
reinterpret_cast<const GLvoid *>(quintptr(ofs)));
f->glEnableVertexAttribArray(GLuint(locationIdx));
if (bindings[bindingIdx].classification() == QRhiVertexInputBinding::PerInstance
&& caps.instancing)
{
f->glVertexAttribDivisor(GLuint(locationIdx), GLuint(bindings[bindingIdx].instanceStepRate()));
}
if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing)
f->glVertexAttribDivisor(GLuint(locationIdx), GLuint(inputBinding->instanceStepRate()));
}
} else {
qWarning("No graphics pipeline active for setVertexInput; ignored");
@ -2510,10 +2512,12 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAttachment.texture());
QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAttachment.resolveTexture());
for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
it != itEnd; ++it)
{
const QRhiColorAttachment &colorAtt(*it);
QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
if (texD) {
trackedRegisterTexture(&passResTracker, texD,
QRhiPassResourceTracker::TexColorOutput,
@ -2602,10 +2606,9 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
if (!colorAttachments.isEmpty()) {
if (rtTex->m_desc.cbeginColorAttachments() != rtTex->m_desc.cendColorAttachments()) {
// handle only 1 color attachment and only (msaa) renderbuffer
const QRhiColorAttachment &colorAtt(colorAttachments[0]);
const QRhiColorAttachment &colorAtt(*rtTex->m_desc.cbeginColorAttachments());
if (colorAtt.resolveTexture()) {
Q_ASSERT(colorAtt.renderBuffer());
QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer());
@ -3446,14 +3449,18 @@ bool QGles2TextureRenderTarget::build()
if (framebuffer)
release();
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
if (colorAttachments.count() > rhiD->caps.maxDrawBuffers)
qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
colorAttachments.count(), rhiD->caps.maxDrawBuffers);
if (hasColorAttachments) {
const int count = m_desc.cendColorAttachments() - m_desc.cbeginColorAttachments();
if (count > rhiD->caps.maxDrawBuffers) {
qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
count, rhiD->caps.maxDrawBuffers);
}
}
if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
@ -3463,9 +3470,11 @@ bool QGles2TextureRenderTarget::build()
rhiD->f->glGenFramebuffers(1, &framebuffer);
rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
d.colorAttCount = colorAttachments.count();
for (int i = 0; i < d.colorAttCount; ++i) {
const QRhiColorAttachment &colorAtt(colorAttachments[i]);
d.colorAttCount = 0;
int attIndex = 0;
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
d.colorAttCount += 1;
const QRhiColorAttachment &colorAtt(*it);
QRhiTexture *texture = colorAtt.texture();
QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
Q_ASSERT(texture || renderBuffer);
@ -3473,16 +3482,16 @@ bool QGles2TextureRenderTarget::build()
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
Q_ASSERT(texD->texture && texD->specified);
const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(i), faceTargetBase + uint(colorAtt.layer()),
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
texD->texture, colorAtt.level());
if (i == 0) {
if (attIndex == 0) {
d.pixelSize = texD->pixelSize();
d.sampleCount = 1;
}
} else if (renderBuffer) {
QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(i), GL_RENDERBUFFER, rbD->renderbuffer);
if (i == 0) {
rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer);
if (attIndex == 0) {
d.pixelSize = rbD->pixelSize();
d.sampleCount = rbD->samples;
}

View File

@ -550,6 +550,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::BaseInstance:
return true;
case QRhi::TriangleFanTopology:
return false;
default:
Q_UNREACHABLE();
return false;
@ -1775,14 +1777,15 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
}
const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
if (colorAttachment.texture())
QRHI_RES(QMetalTexture, colorAttachment.texture())->lastActiveFrameSlot = currentFrameSlot;
else if (colorAttachment.renderBuffer())
QRHI_RES(QMetalRenderBuffer, colorAttachment.renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
if (colorAttachment.resolveTexture())
QRHI_RES(QMetalTexture, colorAttachment.resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
it != itEnd; ++it)
{
if (it->texture())
QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
else if (it->renderBuffer())
QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
if (it->resolveTexture())
QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
}
if (rtTex->m_desc.depthStencilBuffer())
QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
@ -2647,14 +2650,15 @@ void QMetalTextureRenderTarget::release()
QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDescriptor()
{
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
const int colorAttachmentCount = m_desc.cendColorAttachments() - m_desc.cbeginColorAttachments();
QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
rpD->colorAttachmentCount = colorAttachments.count();
rpD->colorAttachmentCount = colorAttachmentCount;
rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
for (int i = 0, ie = colorAttachments.count(); i != ie; ++i) {
QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture());
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer());
for (int i = 0; i < colorAttachmentCount; ++i) {
const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAtt->texture());
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAtt->renderBuffer());
rpD->colorFormat[i] = int(texD ? texD->d->format : rbD->d->format);
}
@ -2668,39 +2672,41 @@ QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDesc
bool QMetalTextureRenderTarget::build()
{
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
d->colorAttCount = colorAttachments.count();
for (int i = 0; i < d->colorAttCount; ++i) {
QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture());
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer());
d->colorAttCount = 0;
int attIndex = 0;
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
d->colorAttCount += 1;
QMetalTexture *texD = QRHI_RES(QMetalTexture, it->texture());
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
id<MTLTexture> dst = nil;
if (texD) {
dst = texD->d->tex;
if (i == 0) {
if (attIndex == 0) {
d->pixelSize = texD->pixelSize();
d->sampleCount = texD->samples;
}
} else if (rbD) {
dst = rbD->d->tex;
if (i == 0) {
if (attIndex == 0) {
d->pixelSize = rbD->pixelSize();
d->sampleCount = rbD->samples;
}
}
QMetalRenderTargetData::ColorAtt colorAtt;
colorAtt.tex = dst;
colorAtt.layer = colorAttachments[i].layer();
colorAtt.level = colorAttachments[i].level();
QMetalTexture *resTexD = QRHI_RES(QMetalTexture, colorAttachments[i].resolveTexture());
colorAtt.layer = it->layer();
colorAtt.level = it->level();
QMetalTexture *resTexD = QRHI_RES(QMetalTexture, it->resolveTexture());
colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
colorAtt.resolveLayer = colorAttachments[i].resolveLayer();
colorAtt.resolveLevel = colorAttachments[i].resolveLevel();
d->fb.colorAtt[i] = colorAtt;
colorAtt.resolveLayer = it->resolveLayer();
colorAtt.resolveLevel = it->resolveLevel();
d->fb.colorAtt[attIndex] = colorAtt;
}
d->dpr = 1;
@ -3119,22 +3125,24 @@ bool QMetalGraphicsPipeline::build()
const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1;
MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
for (const QRhiVertexInputAttribute &attribute : attributes) {
const uint loc = uint(attribute.location());
inputLayout.attributes[loc].format = toMetalAttributeFormat(attribute.format());
inputLayout.attributes[loc].offset = NSUInteger(attribute.offset());
inputLayout.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + attribute.binding());
for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
it != itEnd; ++it)
{
const uint loc = uint(it->location());
inputLayout.attributes[loc].format = toMetalAttributeFormat(it->format());
inputLayout.attributes[loc].offset = NSUInteger(it->offset());
inputLayout.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
}
const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
for (int i = 0, ie = bindings.count(); i != ie; ++i) {
const QRhiVertexInputBinding &binding(bindings[i]);
const uint layoutIdx = uint(firstVertexBinding + i);
int bindingIndex = 0;
for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings();
it != itEnd; ++it, ++bindingIndex)
{
const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
inputLayout.layouts[layoutIdx].stepFunction =
binding.classification() == QRhiVertexInputBinding::PerInstance
it->classification() == QRhiVertexInputBinding::PerInstance
? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
inputLayout.layouts[layoutIdx].stepRate = NSUInteger(binding.instanceStepRate());
inputLayout.layouts[layoutIdx].stride = binding.stride();
inputLayout.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
inputLayout.layouts[layoutIdx].stride = it->stride();
}
MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];

View File

@ -628,10 +628,9 @@ QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescr
bool QNullTextureRenderTarget::build()
{
d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
if (!colorAttachments.isEmpty()) {
QRhiTexture *tex = colorAttachments.first().texture();
QRhiRenderBuffer *rb = colorAttachments.first().renderBuffer();
if (m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments()) {
QRhiTexture *tex = m_desc.cbeginColorAttachments()->texture();
QRhiRenderBuffer *rb = m_desc.cbeginColorAttachments()->renderBuffer();
d.pixelSize = tex ? tex->pixelSize() : rb->pixelSize();
} else if (m_desc.depthStencilBuffer()) {
d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();

View File

@ -568,6 +568,9 @@ bool QRhiVulkan::create(QRhi::Flags flags)
VmaAllocatorCreateInfo allocatorInfo;
memset(&allocatorInfo, 0, sizeof(allocatorInfo));
// A QRhi is supposed to be used from one single thread only. Disable
// the allocator's own mutexes. This gives a performance boost.
allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
allocatorInfo.physicalDevice = physDev;
allocatorInfo.device = dev;
allocatorInfo.pVulkanFunctions = &afuncs;
@ -1111,7 +1114,8 @@ bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil,
}
bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
const QVector<QRhiColorAttachment> &colorAttachments,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
bool preserveDs,
QRhiRenderBuffer *depthStencilBuffer,
@ -1120,13 +1124,12 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
QVarLengthArray<VkAttachmentDescription, 8> attDescs;
QVarLengthArray<VkAttachmentReference, 8> colorRefs;
QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
const int colorAttCount = colorAttachments.count();
// attachment list layout is color (0-8), ds (0-1), resolve (0-8)
for (int i = 0; i < colorAttCount; ++i) {
QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
@ -1136,7 +1139,7 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
attDesc.format = vkformat;
attDesc.samples = samples;
attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc.storeOp = colorAttachments[i].resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
attDesc.storeOp = it->resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
@ -1170,9 +1173,9 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
}
VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
for (int i = 0; i < colorAttCount; ++i) {
if (colorAttachments[i].resolveTexture()) {
QVkTexture *rtexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
if (it->resolveTexture()) {
QVkTexture *rtexD = QRHI_RES(QVkTexture, it->resolveTexture());
if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
qWarning("Resolving into a multisample texture is not supported");
@ -1979,11 +1982,10 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe
rtD->lastActiveFrameSlot = currentFrameSlot;
rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
const QVector<QRhiColorAttachment> colorAttachments = rtD->m_desc.colorAttachments();
for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachment.texture());
QVkTexture *resolveTexD = QRHI_RES(QVkTexture, colorAttachment.resolveTexture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachment.renderBuffer());
for (auto it = rtD->m_desc.cbeginColorAttachments(), itEnd = rtD->m_desc.cendColorAttachments(); it != itEnd; ++it) {
QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
QVkTexture *resolveTexD = QRHI_RES(QVkTexture, it->resolveTexture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
if (texD) {
trackedRegisterTexture(&passResTracker, texD,
QRhiPassResourceTracker::TexColorOutput,
@ -3725,6 +3727,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::BaseInstance:
return true;
case QRhi::TriangleFanTopology:
return true;
default:
Q_UNREACHABLE();
return false;
@ -4570,6 +4574,8 @@ static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case QRhiGraphicsPipeline::TriangleStrip:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case QRhiGraphicsPipeline::TriangleFan:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case QRhiGraphicsPipeline::Lines:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case QRhiGraphicsPipeline::LineStrip:
@ -5487,7 +5493,8 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
QRHI_RES_RHI(QRhiVulkan);
QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
if (!rhiD->createOffscreenRenderPass(&rp->rp,
m_desc.colorAttachments(),
m_desc.cbeginColorAttachments(),
m_desc.cendColorAttachments(),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
m_desc.depthStencilBuffer(),
@ -5507,18 +5514,20 @@ bool QVkTextureRenderTarget::build()
if (d.fb)
release();
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
QRHI_RES_RHI(QRhiVulkan);
QVarLengthArray<VkImageView, 8> views;
d.colorAttCount = colorAttachments.count();
for (int i = 0; i < d.colorAttCount; ++i) {
QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
d.colorAttCount = 0;
int attIndex = 0;
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
d.colorAttCount += 1;
QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
if (texD) {
Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
@ -5533,24 +5542,24 @@ bool QVkTextureRenderTarget::build()
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = uint32_t(colorAttachments[i].level());
viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level());
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = uint32_t(colorAttachments[i].layer());
viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer());
viewInfo.subresourceRange.layerCount = 1;
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[i]);
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]);
if (err != VK_SUCCESS) {
qWarning("Failed to create render target image view: %d", err);
return false;
}
views.append(rtv[i]);
if (i == 0) {
views.append(rtv[attIndex]);
if (attIndex == 0) {
d.pixelSize = texD->pixelSize();
d.sampleCount = texD->samples;
}
} else if (rbD) {
Q_ASSERT(rbD->backingTexture);
views.append(rbD->backingTexture->imageView);
if (i == 0) {
if (attIndex == 0) {
d.pixelSize = rbD->pixelSize();
d.sampleCount = rbD->samples;
}
@ -5580,9 +5589,10 @@ bool QVkTextureRenderTarget::build()
}
d.resolveAttCount = 0;
for (int i = 0; i < d.colorAttCount; ++i) {
if (colorAttachments[i].resolveTexture()) {
QVkTexture *resTexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
attIndex = 0;
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
if (it->resolveTexture()) {
QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture());
Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
d.resolveAttCount += 1;
@ -5597,16 +5607,16 @@ bool QVkTextureRenderTarget::build()
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = uint32_t(colorAttachments[i].resolveLevel());
viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel());
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = uint32_t(colorAttachments[i].resolveLayer());
viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer());
viewInfo.subresourceRange.layerCount = 1;
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[i]);
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]);
if (err != VK_SUCCESS) {
qWarning("Failed to create render target resolve image view: %d", err);
return false;
}
views.append(resrtv[i]);
views.append(resrtv[attIndex]);
}
}
@ -5828,22 +5838,21 @@ bool QVkGraphicsPipeline::build()
pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.count());
pipelineInfo.pStages = shaderStageCreateInfos.constData();
const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
for (int i = 0, ie = bindings.count(); i != ie; ++i) {
const QRhiVertexInputBinding &binding(bindings[i]);
int bindingIndex = 0;
for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings();
it != itEnd; ++it, ++bindingIndex)
{
VkVertexInputBindingDescription bindingInfo = {
uint32_t(i),
binding.stride(),
binding.classification() == QRhiVertexInputBinding::PerVertex
uint32_t(bindingIndex),
it->stride(),
it->classification() == QRhiVertexInputBinding::PerVertex
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
};
if (binding.classification() == QRhiVertexInputBinding::PerInstance
&& binding.instanceStepRate() != 1)
{
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
if (rhiD->vertexAttribDivisorAvailable) {
nonOneStepRates.append({ uint32_t(i), uint32_t(binding.instanceStepRate()) });
nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) });
} else {
qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
"VK_EXT_vertex_attribute_divisor on the device and "
@ -5852,14 +5861,15 @@ bool QVkGraphicsPipeline::build()
}
vertexBindings.append(bindingInfo);
}
const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
QVarLengthArray<VkVertexInputAttributeDescription, 4> vertexAttributes;
for (const QRhiVertexInputAttribute &attribute : attributes) {
for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
it != itEnd; ++it)
{
VkVertexInputAttributeDescription attributeInfo = {
uint32_t(attribute.location()),
uint32_t(attribute.binding()),
toVkAttributeFormat(attribute.format()),
attribute.offset()
uint32_t(it->location()),
uint32_t(it->binding()),
toVkAttributeFormat(it->format()),
it->offset()
};
vertexAttributes.append(attributeInfo);
}

View File

@ -732,7 +732,8 @@ public:
VkSampleCountFlagBits samples,
VkFormat colorFormat);
bool createOffscreenRenderPass(VkRenderPass *rp,
const QVector<QRhiColorAttachment> &colorAttachments,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
bool preserveDs,
QRhiRenderBuffer *depthStencilBuffer,

View File

@ -98,6 +98,20 @@
"condition": "!config.msvc"
}
]
},
"gssapi": {
"label": "KRB5 GSSAPI Support",
"test": {
"main": [
"gss_ctx_id_t ctx;",
"gss_context_time(nullptr, ctx, nullptr);"
]
},
"headers": [ "gssapi/gssapi.h" ],
"sources": [
{ "type": "pkgConfig", "args": "krb5-gssapi" },
"-lgssapi_krb5"
]
}
},
@ -200,15 +214,6 @@
},
"use": "openssl"
},
"gssapi": {
"label": "KRB5 GSSAPI support",
"type": "compile",
"test": {
"include": [ "gssapi/gssapi.h" ],
"main": ["gss_ctx_id_t ctx;"],
"qmake": "LIBS += -lgssapi_krb5"
}
},
"netlistmgr": {
"label": "Network List Manager",
"type": "compile",
@ -408,7 +413,7 @@
"label": "GSSAPI",
"purpose": "Enable SPNEGO authentication through GSSAPI",
"section": "Networking",
"condition": "!config.win32 && tests.gssapi",
"condition": "!config.win32 && libs.gssapi",
"output": [ "publicFeature", "feature" ]
},
"sspi": {

View File

@ -83,7 +83,7 @@ macos | ios {
SOURCES += kernel/qnetconmonitor_stub.cpp
}
qtConfig(gssapi): LIBS_PRIVATE += -lgssapi_krb5
qtConfig(gssapi): QMAKE_USE_PRIVATE += gssapi
uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h
osx:SOURCES += kernel/qnetworkproxy_mac.cpp

View File

@ -26,7 +26,7 @@ qtConfig(bearermanagement) {
ANDROID_BUNDLED_JAR_DEPENDENCIES = \
jar/QtAndroidBearer.jar
ANDROID_LIB_DEPENDENCIES = \
plugins/bearer/libqandroidbearer.so
plugins/bearer/libplugins_bearer_qandroidbearer.so
MODULE_PLUGIN_TYPES = \
bearer
ANDROID_PERMISSIONS += \

View File

@ -1391,6 +1391,8 @@ void QGL2PaintEngineEx::renderHintsChanged()
state()->renderHintsChanged = true;
#if !defined(QT_OPENGL_ES_2)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if (!d->ctx->contextHandle()->isOpenGLES()) {
if ((state()->renderHints & QPainter::Antialiasing)
#if QT_DEPRECATED_SINCE(5, 14)
@ -1401,6 +1403,7 @@ void QGL2PaintEngineEx::renderHintsChanged()
else
d->glDisable(GL_MULTISAMPLE);
}
QT_WARNING_POP
#endif
d->lastTextureUsed = GLuint(-1);

View File

@ -68,7 +68,9 @@ QTsLibMouseHandler::QTsLibMouseHandler(const QString &key,
return;
}
#ifdef TSLIB_VERSION_EVENTPATH /* also introduced in 1.15 */
qCDebug(qLcTsLib) << "tslib device is" << ts_get_eventpath(m_dev);
#endif
m_notify = new QSocketNotifier(ts_fd(m_dev), QSocketNotifier::Read, this);
connect(m_notify, &QSocketNotifier::activated, this, &QTsLibMouseHandler::readMouseData);
}

View File

@ -60,6 +60,7 @@
#include "qandroideventdispatcher.h"
#include <android/api-level.h>
#include <QtCore/qresource.h>
#include <QtCore/qthread.h>
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
@ -75,6 +76,7 @@ static jclass m_applicationClass = nullptr;
static jobject m_classLoaderObject = nullptr;
static jmethodID m_loadClassMethodID = nullptr;
static AAssetManager *m_assetManager = nullptr;
static jobject m_assets = nullptr;
static jobject m_resourcesObj = nullptr;
static jobject m_activityObject = nullptr;
static jmethodID m_createSurfaceMethodID = nullptr;
@ -439,6 +441,11 @@ namespace QtAndroid
return block;
}
jobject assets()
{
return m_assets;
}
} // namespace QtAndroid
static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
@ -519,6 +526,10 @@ static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
vm->AttachCurrentThread(&env, &args);
}
// Register resources if they are available
if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
QVarLengthArray<const char *> params(m_applicationParams.size());
for (int i = 0; i < m_applicationParams.size(); i++)
params[i] = static_cast<const char *>(m_applicationParams[i].constData());
@ -588,6 +599,8 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
if (m_bitmapDrawableClass)
env->DeleteGlobalRef(m_bitmapDrawableClass);
if (m_assets)
env->DeleteGlobalRef(m_assets);
m_androidPlatformIntegration = nullptr;
delete m_androidAssetsFileEngineHandler;
m_androidAssetsFileEngineHandler = nullptr;
@ -840,7 +853,8 @@ static int registerNatives(JNIEnv *env)
if (object) {
FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(object, methodID));
m_assets = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
m_assetManager = AAssetManager_fromJava(env, m_assets);
GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID));

View File

@ -82,6 +82,7 @@ namespace QtAndroid
double scaledDensity();
double pixelDensity();
JavaVM *javaVM();
jobject assets();
AAssetManager *assetManager();
jclass applicationClass();
jobject activity();

View File

@ -39,40 +39,139 @@
#include "qandroidassetsfileenginehandler.h"
#include "androidjnimain.h"
#include <optional>
#include <QCoreApplication>
#include <QVector>
#include <QtCore/private/qjni_p.h>
QT_BEGIN_NAMESPACE
typedef QVector<QString> FilesList;
static const QLatin1String assetsPrefix("assets:");
const static int prefixSize = 7;
struct AndroidAssetDir
static inline QString cleanedAssetPath(QString file)
{
AndroidAssetDir(AAssetDir* ad)
if (file.startsWith(assetsPrefix))
file.remove(0, prefixSize);
file.replace(QLatin1String("//"), QLatin1String("/"));
if (file.startsWith(QLatin1Char('/')))
file.remove(0, 1);
if (file.endsWith(QLatin1Char('/')))
file.chop(1);
return file;
}
static inline QString prefixedPath(QString path)
{
path = assetsPrefix + QLatin1Char('/') + path;
path.replace(QLatin1String("//"), QLatin1String("/"));
return path;
}
struct AssetItem {
enum class Type {
File,
Folder
};
AssetItem (const QString &rawName)
: name(rawName)
{
if (ad) {
const char *fileName;
while ((fileName = AAssetDir_getNextFileName(ad)))
m_items.push_back(QString::fromUtf8(fileName));
AAssetDir_close(ad);
if (name.endsWith(QLatin1Char('/'))) {
type = Type::Folder;
name.chop(1);
}
}
FilesList m_items;
Type type = Type::File;
QString name;
};
using AssetItemList = QVector<AssetItem>;
class FolderIterator : public AssetItemList
{
public:
static QSharedPointer<FolderIterator> fromCache(const QString &path)
{
QMutexLocker lock(&m_assetsCacheMutex);
QSharedPointer<FolderIterator> *folder = m_assetsCache.object(path);
if (!folder) {
folder = new QSharedPointer<FolderIterator>{new FolderIterator{path}};
if (!m_assetsCache.insert(path, folder)) {
QSharedPointer<FolderIterator> res = *folder;
delete folder;
return res;
}
}
return *folder;
}
FolderIterator(const QString &path)
: m_path(path)
{
QJNIObjectPrivate files = QJNIObjectPrivate::callStaticObjectMethod(QtAndroid::applicationClass(),
"listAssetContent",
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
QtAndroid::assets(), QJNIObjectPrivate::fromString(path).object());
if (files.isValid()) {
QJNIEnvironmentPrivate env;
jobjectArray jFiles = static_cast<jobjectArray>(files.object());
const jint nFiles = env->GetArrayLength(jFiles);
for (int i = 0; i < nFiles; ++i)
push_back({QJNIObjectPrivate(env->GetObjectArrayElement(jFiles, i)).toString()});
}
m_path = assetsPrefix + QLatin1Char('/') + m_path + QLatin1Char('/');
m_path.replace(QLatin1String("//"), QLatin1String("/"));
}
QString currentFileName() const
{
if (m_index < 0 || m_index >= size())
return {};
return at(m_index).name;
}
QString currentFilePath() const
{
if (m_index < 0 || m_index >= size())
return {};
return m_path + at(m_index).name;
}
bool hasNext() const
{
return !empty() && m_index + 1 < size();
}
std::optional<std::pair<QString, AssetItem>> next()
{
if (!hasNext())
return {};
++m_index;
return std::pair<QString, AssetItem>(currentFileName(), at(m_index));
}
private:
int m_index = -1;
QString m_path;
static QCache<QString, QSharedPointer<FolderIterator>> m_assetsCache;
static QMutex m_assetsCacheMutex;
};
QCache<QString, QSharedPointer<FolderIterator>> FolderIterator::m_assetsCache(std::max(50, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE")));
QMutex FolderIterator::m_assetsCacheMutex;
class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator
{
public:
AndroidAbstractFileEngineIterator(QDir::Filters filters,
const QStringList &nameFilters,
QSharedPointer<AndroidAssetDir> asset,
const QString &path)
: QAbstractFileEngineIterator(filters, nameFilters)
{
m_items = asset->m_items;
m_index = -1;
m_path = path;
m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(path)));
if (m_stack.last()->empty())
m_stack.pop_back();
}
QFileInfo currentFileInfo() const override
@ -82,54 +181,59 @@ public:
QString currentFileName() const override
{
if (m_index < 0 || m_index >= m_items.size())
return QString();
QString fileName = m_items[m_index];
if (fileName.endsWith(QLatin1Char('/')))
fileName.chop(1);
return fileName;
if (!m_currentIterator)
return {};
return m_currentIterator->currentFileName();
}
virtual QString currentFilePath() const
{
return m_path + currentFileName();
if (!m_currentIterator)
return {};
return m_currentIterator->currentFilePath();
}
bool hasNext() const override
{
return m_items.size() && (m_index < m_items.size() - 1);
if (m_stack.empty())
return false;
if (!m_stack.last()->hasNext()) {
m_stack.pop_back();
return hasNext();
}
return true;
}
QString next() override
{
if (!hasNext())
return QString();
m_index++;
return currentFileName();
if (m_stack.empty()) {
m_currentIterator.reset();
return {};
}
m_currentIterator = m_stack.last();
auto res = m_currentIterator->next();
if (!res)
return {};
if (res->second.type == AssetItem::Type::Folder) {
m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(currentFilePath())));
if (m_stack.last()->empty())
m_stack.pop_back();
}
return res->first;
}
private:
QString m_path;
FilesList m_items;
int m_index;
mutable QSharedPointer<FolderIterator> m_currentIterator;
mutable QVector<QSharedPointer<FolderIterator>> m_stack;
};
class AndroidAbstractFileEngine: public QAbstractFileEngine
{
public:
explicit AndroidAbstractFileEngine(AAsset *asset, const QString &fileName)
explicit AndroidAbstractFileEngine(AAssetManager *assetManager, const QString &fileName)
: m_assetManager(assetManager)
{
m_assetFile = asset;
m_fileName = fileName;
}
explicit AndroidAbstractFileEngine(QSharedPointer<AndroidAssetDir> asset, const QString &fileName)
{
m_assetFile = 0;
m_assetDir = asset;
m_fileName = fileName;
if (!m_fileName.endsWith(QLatin1Char('/')))
m_fileName += QLatin1Char('/');
setFileName(fileName);
}
~AndroidAbstractFileEngine()
@ -139,7 +243,11 @@ public:
bool open(QIODevice::OpenMode openMode) override
{
return m_assetFile != 0 && (openMode & QIODevice::WriteOnly) == 0;
if (m_isFolder || (openMode & QIODevice::WriteOnly))
return false;
close();
m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
return m_assetFile;
}
bool close() override
@ -200,7 +308,7 @@ public:
FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
if (m_assetFile)
flags |= FileType;
if (!m_assetDir.isNull())
else if (m_isFolder)
flags |= DirectoryType;
return type & flags;
@ -213,19 +321,19 @@ public:
case DefaultName:
case AbsoluteName:
case CanonicalName:
return m_fileName;
return prefixedPath(m_fileName);
case BaseName:
if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
return m_fileName.mid(pos);
return prefixedPath(m_fileName.mid(pos));
else
return m_fileName;
return prefixedPath(m_fileName);
case PathName:
case AbsolutePathName:
case CanonicalPathName:
if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
return m_fileName.left(pos);
return prefixedPath(m_fileName.left(pos));
else
return m_fileName;
return prefixedPath(m_fileName);
default:
return QString();
}
@ -233,164 +341,46 @@ public:
void setFileName(const QString &file) override
{
if (file == m_fileName)
return;
m_fileName = file;
if (!m_fileName.endsWith(QLatin1Char('/')))
m_fileName += QLatin1Char('/');
close();
m_fileName = cleanedAssetPath(file);
m_isFolder = !open(QIODevice::ReadOnly) && !FolderIterator::fromCache(m_fileName)->empty();
}
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
{
if (!m_assetDir.isNull())
return new AndroidAbstractFileEngineIterator(filters, filterNames, m_assetDir, m_fileName);
return 0;
if (m_isFolder)
return new AndroidAbstractFileEngineIterator(filters, filterNames, m_fileName);
return nullptr;
}
private:
AAsset *m_assetFile;
QSharedPointer<AndroidAssetDir> m_assetDir;
AAsset *m_assetFile = nullptr;
AAssetManager *m_assetManager;
QString m_fileName;
bool m_isFolder;
};
AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler()
: m_assetsCache(std::max(5, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE")))
, m_hasPrepopulatedCache(false)
, m_hasTriedPrepopulatingCache(false)
{
m_assetManager = QtAndroid::assetManager();
}
AndroidAssetsFileEngineHandler::~AndroidAssetsFileEngineHandler()
{
}
void AndroidAssetsFileEngineHandler::prepopulateCache() const
{
Q_ASSERT(!m_hasTriedPrepopulatingCache);
m_hasTriedPrepopulatingCache = true;
Q_ASSERT(m_assetsCache.isEmpty());
// Failsafe: Don't read cache files that are larger than 1MB
static qint64 maxPrepopulatedCacheSize = qMax(1024LL * 1024LL,
qgetenv("QT_ANDROID_MAX_PREPOPULATED_ASSETS_CACHE_SIZE").toLongLong());
const char *fileName = "--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list";
AAsset *asset = AAssetManager_open(m_assetManager, fileName, AASSET_MODE_BUFFER);
if (asset) {
m_hasPrepopulatedCache = true;
AndroidAbstractFileEngine fileEngine(asset, QString::fromLatin1(fileName));
if (fileEngine.open(QIODevice::ReadOnly)) {
qint64 size = fileEngine.size();
if (size <= maxPrepopulatedCacheSize) {
QByteArray bytes(size, Qt::Uninitialized);
qint64 read = fileEngine.read(bytes.data(), size);
if (read != size) {
qWarning("Failed to read prepopulated cache");
return;
}
QDataStream stream(&bytes, QIODevice::ReadOnly);
stream.setVersion(QDataStream::Qt_5_3);
if (stream.status() != QDataStream::Ok) {
qWarning("Failed to read prepopulated cache");
return;
}
while (!stream.atEnd()) {
QString directoryName;
stream >> directoryName;
int fileCount;
stream >> fileCount;
QVector<QString> fileList;
fileList.reserve(fileCount);
while (fileCount--) {
QString fileName;
stream >> fileName;
fileList.append(fileName);
}
QSharedPointer<AndroidAssetDir> *aad = new QSharedPointer<AndroidAssetDir>(new AndroidAssetDir(0));
(*aad)->m_items = fileList;
// Cost = 0, because we should always cache everything if there's a prepopulated cache
QByteArray key = directoryName != QLatin1String("/")
? QByteArray("assets:/") + directoryName.toUtf8()
: QByteArray("assets:");
bool ok = m_assetsCache.insert(key, aad, 0);
if (!ok)
qWarning("Failed to insert in cache: %s", qPrintable(directoryName));
}
} else {
qWarning("Prepopulated cache is too large to read.\n"
"Use environment variable QT_ANDROID_MAX_PREPOPULATED_ASSETS_CACHE_SIZE to adjust size.");
}
}
}
}
QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &fileName) const
{
if (fileName.isEmpty())
return 0;
return nullptr;
static QLatin1String assetsPrefix("assets:");
if (!fileName.startsWith(assetsPrefix))
return 0;
return nullptr;
static int prefixSize = assetsPrefix.size() + 1;
QByteArray path;
if (!fileName.endsWith(QLatin1Char('/'))) {
path = fileName.toUtf8();
if (path.size() > prefixSize) {
AAsset *asset = AAssetManager_open(m_assetManager,
path.constData() + prefixSize,
AASSET_MODE_BUFFER);
if (asset)
return new AndroidAbstractFileEngine(asset, fileName);
}
}
if (!path.size())
path = fileName.left(fileName.length() - 1).toUtf8();
m_assetsCacheMutext.lock();
if (!m_hasTriedPrepopulatingCache)
prepopulateCache();
QSharedPointer<AndroidAssetDir> *aad = m_assetsCache.object(path);
m_assetsCacheMutext.unlock();
if (!aad) {
if (!m_hasPrepopulatedCache && path.size() > prefixSize) {
AAssetDir *assetDir = AAssetManager_openDir(m_assetManager, path.constData() + prefixSize);
if (assetDir) {
if (AAssetDir_getNextFileName(assetDir)) {
AAssetDir_rewind(assetDir);
aad = new QSharedPointer<AndroidAssetDir>(new AndroidAssetDir(assetDir));
m_assetsCacheMutext.lock();
m_assetsCache.insert(path, aad);
m_assetsCacheMutext.unlock();
return new AndroidAbstractFileEngine(*aad, fileName);
} else {
AAssetDir_close(assetDir);
}
}
}
} else {
return new AndroidAbstractFileEngine(*aad, fileName);
}
return 0;
QString path = fileName.mid(prefixSize);
path.replace(QLatin1String("//"), QLatin1String("/"));
if (path.startsWith(QLatin1Char('/')))
path.remove(0, 1);
if (path.endsWith(QLatin1Char('/')))
path.chop(1);
return new AndroidAbstractFileEngine(m_assetManager, path);
}
QT_END_NAMESPACE

View File

@ -49,22 +49,14 @@
QT_BEGIN_NAMESPACE
struct AndroidAssetDir;
class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler
{
public:
AndroidAssetsFileEngineHandler();
virtual ~AndroidAssetsFileEngineHandler();
QAbstractFileEngine *create(const QString &fileName) const override;
private:
void prepopulateCache() const;
AAssetManager *m_assetManager;
mutable QCache<QByteArray, QSharedPointer<AndroidAssetDir>> m_assetsCache;
mutable QMutex m_assetsCacheMutext;
mutable bool m_hasPrepopulatedCache;
mutable bool m_hasTriedPrepopulatingCache;
};
QT_END_NAMESPACE

View File

@ -941,6 +941,8 @@ public:
{
Q_Q(QWindowsDirect2DPaintEngine);
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
// Default path (no optimization)
if (!(path.shape() == QVectorPath::LinesHint || path.shape() == QVectorPath::PolygonHint)
|| !pen.dashBrush
@ -948,6 +950,7 @@ public:
|| q->state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)
#endif
|| q->state()->renderHints.testFlag(QPainter::Antialiasing)) {
QT_WARNING_POP
ComPtr<ID2D1Geometry> geometry = vectorPathToID2D1PathGeometry(path);
if (!geometry) {
qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__);

View File

@ -211,6 +211,8 @@ namespace
} logActivity;
}
using namespace QT_PREPEND_NAMESPACE(QtPrivate);
extern "C" int qt_main_wrapper(int argc, char *argv[])
{
@autoreleasepool {

View File

@ -812,8 +812,8 @@ QSqlRecord QPSQLResult::record() const
return info;
int count = PQnfields(d->result);
QSqlField f;
for (int i = 0; i < count; ++i) {
QSqlField f;
if (d->drv_d_func()->isUtf8)
f.setName(QString::fromUtf8(PQfname(d->result, i)));
else
@ -833,6 +833,8 @@ QSqlRecord QPSQLResult::record() const
}
}
f.setTableName(tableName);
} else {
f.setTableName(QString());
}
int ptype = PQftype(d->result, i);
f.setType(qDecodePSQLType(ptype));

View File

@ -116,7 +116,6 @@ struct Options
: helpRequested(false)
, verbose(false)
, timing(false)
, generateAssetsFileList(true)
, build(true)
, auxMode(false)
, deploymentMechanism(Bundled)
@ -146,7 +145,6 @@ struct Options
bool helpRequested;
bool verbose;
bool timing;
bool generateAssetsFileList;
bool build;
bool auxMode;
ActionTimer timer;
@ -314,10 +312,8 @@ static QString shellQuote(const QString &arg)
QString architecureFromName(const QString &name)
{
const QFileInfo fi(name);
const QString extractedFileName = fi.fileName();
QRegExp architecture(QStringLiteral(".*_(.*)\\.so"));
if (!architecture.exactMatch(extractedFileName))
QRegExp architecture(QStringLiteral(".*_(armeabi-v7a|arm64-v8a|x86|x86_64).so"));
if (!architecture.exactMatch(name))
return {};
return architecture.capturedTexts().last();
}
@ -523,8 +519,6 @@ Options parseOptions()
options.protectedAuthenticationPath = true;
} else if (argument.compare(QLatin1String("--jarsigner"), Qt::CaseInsensitive) == 0) {
options.jarSigner = true;
} else if (argument.compare(QLatin1String("--no-generated-assets-cache"), Qt::CaseInsensitive) == 0) {
options.generateAssetsFileList = false;
} else if (argument.compare(QLatin1String("--aux-mode"), Qt::CaseInsensitive) == 0) {
options.auxMode = true;
}
@ -1177,7 +1171,7 @@ bool copyAndroidExtraResources(Options *options)
} else {
if (!checkArchitecture(*options, originFile))
continue;
destinationFile = libsDir + QLatin1String("/lib") + QString(resourceDir.dirName() + QLatin1Char('/') + resourceFile).replace(QLatin1Char('/'), QLatin1Char('_'));
destinationFile = libsDir + resourceFile;
options->archExtraPlugins[options->currentArchitecture] += resourceFile;
}
if (!copyFileIfNewer(originFile, destinationFile, *options))
@ -1244,8 +1238,6 @@ bool updateLibsXml(Options *options)
}
QString qtLibs;
QString bundledInLibs;
QString bundledInAssets;
QString allLocalLibs;
QString extraLibs;
@ -1258,33 +1250,6 @@ bool updateLibsXml(Options *options)
QString s = bundledFile.second.mid(sizeof("lib/lib") - 1);
s.chop(sizeof(".so") - 1);
qtLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), s);
} else if (bundledFile.first.startsWith(libsPath)) {
QString s = bundledFile.first.mid(libsPath.length());
bundledInLibs += QString::fromLatin1(" <item>%1;%2:%3</item>\n")
.arg(it.key(), s, bundledFile.second);
} else if (bundledFile.first.startsWith(QLatin1String("assets/"))) {
QString s = bundledFile.first.mid(sizeof("assets/") - 1);
bundledInAssets += QString::fromLatin1(" <item>%1:%2</item>\n")
.arg(s, bundledFile.second);
}
}
if (!options->archExtraPlugins[it.key()].isEmpty()) {
for (const QString &extraRes : options->archExtraPlugins[it.key()]) {
QDir resourceDir(extraRes);
const QStringList files = allFilesInside(resourceDir, resourceDir);
for (const QString &file : files) {
QString destinationPath = resourceDir.dirName() + QLatin1Char('/') + file;
if (!file.endsWith(QLatin1String(".so"))) {
bundledInAssets += QLatin1String(" <item>%1:%1</item>\n")
.arg(destinationPath);
} else {
bundledInLibs += QLatin1String(" <item>%1;lib%2:%3</item>\n")
.arg(it.key(),
QString(destinationPath).replace(QLatin1Char('/'), QLatin1Char('_')),
destinationPath);
}
}
}
}
@ -1330,9 +1295,13 @@ bool updateLibsXml(Options *options)
if (options->verbose)
fprintf(stdout, " -- Using platform plugin %s\n", qPrintable(plugin));
}
allLocalLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), localLibs.join(QLatin1Char(':'))
.replace(QLatin1String("lib/"), QString{})
.replace(QLatin1Char('/'), QLatin1Char('_')));
// remove all paths
for (auto &lib : localLibs) {
if (lib.endsWith(QLatin1String(".so")))
lib = lib.mid(lib.lastIndexOf(QLatin1Char('/')) + 1);
}
allLocalLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), localLibs.join(QLatin1Char(':')));
}
QHash<QString, QString> replacements;
@ -1340,11 +1309,6 @@ bool updateLibsXml(Options *options)
replacements[QStringLiteral("<!-- %%INSERT_LOCAL_LIBS%% -->")] = allLocalLibs.trimmed();
replacements[QStringLiteral("<!-- %%INSERT_EXTRA_LIBS%% -->")] = extraLibs.trimmed();
if (options->deploymentMechanism == Options::Bundled) {
replacements[QStringLiteral("<!-- %%INSERT_BUNDLED_IN_LIB%% -->")] += bundledInLibs.trimmed();
replacements[QStringLiteral("<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->")] += bundledInAssets.trimmed();
}
if (!updateFile(fileName, replacements))
return false;
@ -1869,6 +1833,70 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
return true;
}
bool runCommand(const Options &options, const QString &command)
{
if (options.verbose)
fprintf(stdout, "Running command '%s'\n", qPrintable(command));
FILE *runCommand = openProcess(command);
if (runCommand == nullptr) {
fprintf(stderr, "Cannot run command '%s'\n", qPrintable(command));
return false;
}
char buffer[4096];
while (fgets(buffer, sizeof(buffer), runCommand) != nullptr) {
if (options.verbose)
fprintf(stdout, "%s", buffer);
}
pclose(runCommand);
fflush(stdout);
fflush(stderr);
return true;
}
bool createRcc(const Options &options)
{
auto assetsDir = QLatin1String("%1/assets").arg(options.outputDirectory);
if (!QDir{QLatin1String("%1/android_rcc_bundle").arg(assetsDir)}.exists()) {
fprintf(stdout, "Skipping createRCC\n");
return true;
}
if (options.verbose)
fprintf(stdout, "Create rcc bundle.\n");
QString rcc = options.qtInstallDirectory + QLatin1String("/bin/rcc");
#if defined(Q_OS_WIN32)
rcc += QLatin1String(".exe");
#endif
if (!QFile::exists(rcc)) {
fprintf(stderr, "rcc not found: %s\n", qPrintable(rcc));
return false;
}
auto currentDir = QDir::currentPath();
if (!QDir::setCurrent(QLatin1String("%1/android_rcc_bundle").arg(assetsDir))) {
fprintf(stderr, "Cannot set current dir to: %s\n", qPrintable(QLatin1String("%1/android_rcc_bundle").arg(assetsDir)));
return false;
}
bool res = runCommand(options, QLatin1String("%1 --project -o %2").arg(rcc, shellQuote(QLatin1String("%1/android_rcc_bundle.qrc").arg(assetsDir))));
if (!res)
return false;
QFile::rename(QLatin1String("%1/android_rcc_bundle.qrc").arg(assetsDir), QLatin1String("%1/android_rcc_bundle/android_rcc_bundle.qrc").arg(assetsDir));
res = runCommand(options, QLatin1String("%1 %2 --binary -o %3 android_rcc_bundle.qrc").arg(rcc, shellQuote(QLatin1String("--root=/android_rcc_bundle/")),
shellQuote(QLatin1String("%1/android_rcc_bundle.rcc").arg(assetsDir))));
if (!QDir::setCurrent(currentDir)) {
fprintf(stderr, "Cannot set current dir to: %s\n", qPrintable(currentDir));
return false;
}
QFile::remove(QLatin1String("%1/android_rcc_bundle.qrc").arg(assetsDir));
QDir{QLatin1String("%1/android_rcc_bundle").arg(assetsDir)}.removeRecursively();
return res;
}
bool readDependencies(Options *options)
{
if (options->verbose)
@ -2023,7 +2051,7 @@ bool copyQtFiles(Options *options)
QString libsDirectory = QLatin1String("libs/");
// Copy other Qt dependencies
auto assetsDestinationDirectory = QLatin1String("assets/--Added-by-androiddeployqt--/");
auto assetsDestinationDirectory = QLatin1String("assets/android_rcc_bundle/");
for (const QtDependency &qtDependency : qAsConst(options->qtDependencies[options->currentArchitecture])) {
QString sourceFileName = qtDependency.absolutePath;
QString destinationFileName;
@ -2033,7 +2061,7 @@ bool copyQtFiles(Options *options)
if (qtDependency.relativePath.startsWith(QLatin1String("lib/"))) {
garbledFileName = qtDependency.relativePath.mid(sizeof("lib/") - 1);
} else {
garbledFileName = QString(qtDependency.relativePath).replace(QLatin1Char('/'), QLatin1Char('_'));
garbledFileName = qtDependency.relativePath.mid(qtDependency.relativePath.lastIndexOf(QLatin1Char('/')) + 1);
}
destinationFileName = libsDirectory + options->currentArchitecture + QLatin1Char('/') + garbledFileName;
} else if (qtDependency.relativePath.startsWith(QLatin1String("jar/"))) {
@ -2679,57 +2707,6 @@ bool signPackage(const Options &options)
return apkSignerRunner() && QFile::remove(packagePath(options, UnsignedAPK));
}
bool generateAssetsFileList(const Options &options)
{
if (options.verbose)
fprintf(stdout, "Pregenerating entry list for assets file engine.\n");
QString assetsPath = options.outputDirectory + QLatin1String("/assets/");
QString addedByAndroidDeployQtPath = assetsPath + QLatin1String("--Added-by-androiddeployqt--/");
if (!QDir().mkpath(addedByAndroidDeployQtPath)) {
fprintf(stderr, "Failed to create directory '%s'", qPrintable(addedByAndroidDeployQtPath));
return false;
}
QFile file(addedByAndroidDeployQtPath + QLatin1String("/qt_cache_pregenerated_file_list"));
if (file.open(QIODevice::WriteOnly)) {
QDirIterator dirIterator(assetsPath,
QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
QHash<QString, QStringList> directoryContents;
while (dirIterator.hasNext()) {
const QString name = dirIterator.next().mid(assetsPath.length());
int slashIndex = name.lastIndexOf(QLatin1Char('/'));
QString pathName = slashIndex >= 0 ? name.left(slashIndex) : QStringLiteral("/");
QString fileName = slashIndex >= 0 ? name.mid(pathName.length() + 1) : name;
if (!fileName.isEmpty() && dirIterator.fileInfo().isDir() && !fileName.endsWith(QLatin1Char('/')))
fileName += QLatin1Char('/');
if (fileName.isEmpty() && !directoryContents.contains(pathName))
directoryContents[pathName] = QStringList();
else if (!fileName.isEmpty())
directoryContents[pathName].append(fileName);
}
QDataStream stream(&file);
stream.setVersion(QDataStream::Qt_5_3);
for (auto it = directoryContents.cbegin(), end = directoryContents.cend(); it != end; ++it) {
const QStringList &entryList = it.value();
stream << it.key() << entryList.size();
for (const QString &entry : entryList)
stream << entry;
}
} else {
fprintf(stderr, "Pregenerating entry list for assets file engine failed!\n");
return false;
}
return true;
}
enum ErrorCode
{
Success,
@ -2747,9 +2724,9 @@ enum ErrorCode
CannotBuildAndroidProject = 14,
CannotSignPackage = 15,
CannotInstallApk = 16,
CannotGenerateAssetsFileList = 18,
CannotCopyAndroidExtraResources = 19,
CannotCopyApk = 20
CannotCopyApk = 20,
CannotCreateRcc = 21
};
int main(int argc, char *argv[])
@ -2846,14 +2823,16 @@ int main(int argc, char *argv[])
}
}
if (!createRcc(options))
return CannotCreateRcc;
if (options.auxMode) {
if (!updateAndroidFiles(options))
return CannotUpdateAndroidFiles;
if (options.generateAssetsFileList && !generateAssetsFileList(options))
return CannotGenerateAssetsFileList;
return 0;
}
if (options.build) {
if (!copyAndroidSources(options))
return CannotCopyAndroidSources;
@ -2864,9 +2843,6 @@ int main(int argc, char *argv[])
if (!updateAndroidFiles(options))
return CannotUpdateAndroidFiles;
if (options.generateAssetsFileList && !generateAssetsFileList(options))
return CannotGenerateAssetsFileList;
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Updated files\n", options.timer.elapsed());

View File

@ -3224,8 +3224,11 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos);
#if QT_DEPRECATED_SINCE(5, 14)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(),
wheel->modifiers(), phase, wheel->source(), wheel->inverted());
QT_WARNING_POP
#else
QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(),
wheel->modifiers(), phase, wheel->inverted(), wheel->source());
@ -3266,8 +3269,11 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
// the end of the natural scrolling sequence.
const QPoint &relpos = QApplicationPrivate::wheel_widget->mapFromGlobal(wheel->globalPosition().toPoint());
#if QT_DEPRECATED_SINCE(5, 0)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(),
wheel->modifiers(), wheel->phase(), wheel->source());
QT_WARNING_POP
#else
QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(),
wheel->modifiers(), wheel->phase(), wheel->inverted(), wheel->source());

View File

@ -41,7 +41,8 @@ private slots:
void fuzzyCompare();
void ltgt_data();
void ltgt();
void qNan();
void qNaN();
void infinity();
void float_cast();
void float_cast_data();
void promotionTests();
@ -49,6 +50,9 @@ private slots:
void arithOps();
void floatToFloat16();
void floatFromFloat16();
void finite_data();
void finite();
void properties();
void limits();
};
@ -154,38 +158,66 @@ void tst_qfloat16::ltgt()
# pragma GCC optimize "no-fast-math"
#endif
void tst_qfloat16::qNan()
void tst_qfloat16::qNaN()
{
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404)
QSKIP("Non-conformant fast math mode is enabled, cannot run test");
#endif
qfloat16 nan = qQNaN();
QVERIFY(!(0. > nan));
QVERIFY(!(0. < nan));
using Bounds = std::numeric_limits<qfloat16>;
const qfloat16 nan = Bounds::quiet_NaN();
const qfloat16 zero(0), one(1);
QVERIFY(!(zero > nan));
QVERIFY(!(zero < nan));
QVERIFY(!(zero == nan));
QVERIFY(!qIsInf(nan));
QVERIFY(qIsNaN(nan));
QVERIFY(qIsNaN(nan + 1.f));
QVERIFY(qIsNaN(nan + one));
QVERIFY(qIsNaN(-nan));
qfloat16 inf = qInf();
QVERIFY(inf > qfloat16(0));
QVERIFY(-inf < qfloat16(0));
QVERIFY(!qIsNaN(inf));
QVERIFY(qIsInf(inf));
QVERIFY(qIsInf(-inf));
QVERIFY(qIsInf(2.f*inf));
QVERIFY(qIsInf(inf*2.f));
// QTBUG-75812: QEMU/arm64 compiler over-optimizes, so flakily fails 1/inf == 0 :-(
if (qfloat16(9.785e-4f) == qfloat16(9.794e-4f))
QCOMPARE(qfloat16(1.f) / inf, qfloat16(0.f));
#ifdef Q_CC_INTEL
QEXPECT_FAIL("", "ICC optimizes zero * anything to zero", Continue);
#endif
QVERIFY(qIsNaN(nan*0.f));
QVERIFY(qIsNaN(nan * zero));
#ifdef Q_CC_INTEL
QEXPECT_FAIL("", "ICC optimizes zero * anything to zero", Continue);
#endif
QVERIFY(qIsNaN(inf*0.f));
QVERIFY(qFuzzyCompare(qfloat16(1.f/inf), qfloat16(0.0)));
QVERIFY(qIsNaN(Bounds::infinity() * zero));
QVERIFY(!nan.isNormal());
QVERIFY(!qIsFinite(nan));
QVERIFY(!(nan == nan));
QCOMPARE(nan, nan); // Despite the preceding
QCOMPARE(qFpClassify(nan), FP_NAN);
}
void tst_qfloat16::infinity()
{
const qfloat16 huge = std::numeric_limits<qfloat16>::infinity();
const qfloat16 zero(0), one(1), two(2);
QVERIFY(huge > -huge);
QVERIFY(huge > zero);
QVERIFY(-huge < zero);
QCOMPARE(huge, huge);
QCOMPARE(-huge, -huge);
// QTBUG-75812 - see overOptimized in the limits() test.
if (qfloat16(9.785e-4f) == qfloat16(9.794e-4f)) {
QCOMPARE(one / huge, zero);
QVERIFY(qFuzzyCompare(one / huge, zero)); // (same thing)
}
QVERIFY(qIsInf(huge));
QVERIFY(qIsInf(-huge));
QVERIFY(qIsInf(two * huge));
QVERIFY(qIsInf(huge * two));
QVERIFY(!huge.isNormal());
QVERIFY(!(-huge).isNormal());
QVERIFY(!qIsNaN(huge));
QVERIFY(!qIsNaN(-huge));
QVERIFY(!qIsFinite(huge));
QVERIFY(!qIsFinite(-huge));
QCOMPARE(qFpClassify(huge), FP_INFINITE);
QCOMPARE(qFpClassify(-huge), FP_INFINITE);
}
void tst_qfloat16::float_cast_data()
@ -366,10 +398,40 @@ static qfloat16 powf16(qfloat16 base, int raise)
return answer;
}
void tst_qfloat16::limits()
void tst_qfloat16::finite_data()
{
using Bounds = std::numeric_limits<qfloat16>;
QTest::addColumn<qfloat16>("value");
QTest::addColumn<int>("mode");
QTest::newRow("zero") << qfloat16(0) << FP_ZERO;
QTest::newRow("one") << qfloat16(1) << FP_NORMAL;
QTest::newRow("-one") << qfloat16(-1) << FP_NORMAL;
QTest::newRow("ten") << qfloat16(10) << FP_NORMAL;
QTest::newRow("-ten") << qfloat16(-10) << FP_NORMAL;
QTest::newRow("max") << Bounds::max() << FP_NORMAL;
QTest::newRow("lowest") << Bounds::lowest() << FP_NORMAL;
QTest::newRow("min") << Bounds::min() << FP_NORMAL;
QTest::newRow("-min") << -Bounds::min() << FP_NORMAL;
QTest::newRow("denorm_min") << Bounds::denorm_min() << FP_SUBNORMAL;
QTest::newRow("-denorm_min") << -Bounds::denorm_min() << FP_SUBNORMAL;
}
void tst_qfloat16::finite()
{
QFETCH(qfloat16, value);
QFETCH(int, mode);
QCOMPARE(value.isNormal(), mode != FP_SUBNORMAL);
QCOMPARE(value, value); // Fuzzy
QVERIFY(value == value); // Exact
QVERIFY(qIsFinite(value));
QVERIFY(!qIsInf(value));
QVERIFY(!qIsNaN(value));
QCOMPARE(qFpClassify(value), mode);
}
void tst_qfloat16::properties()
{
// *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy
// comparison, and we need exact here.
using Bounds = std::numeric_limits<qfloat16>;
QVERIFY(Bounds::is_specialized);
QVERIFY(Bounds::is_signed);
@ -386,21 +448,16 @@ void tst_qfloat16::limits()
QCOMPARE(Bounds::round_style, std::round_to_nearest);
QCOMPARE(Bounds::radix, 2);
// Untested: has_denorm_loss
}
// A few common values:
void tst_qfloat16::limits() // See also: qNaN() and infinity()
{
// *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy
// comparison, and we need exact here.
using Bounds = std::numeric_limits<qfloat16>;
// A few useful values:
const qfloat16 zero(0), one(1), ten(10);
QVERIFY(qIsFinite(zero));
QVERIFY(!qIsInf(zero));
QVERIFY(!qIsNaN(zero));
QCOMPARE(qFpClassify(zero), FP_ZERO);
QVERIFY(qIsFinite(one));
QVERIFY(!qIsInf(one));
QCOMPARE(qFpClassify(one), FP_NORMAL);
QVERIFY(!qIsNaN(one));
QVERIFY(qIsFinite(ten));
QVERIFY(!qIsInf(ten));
QVERIFY(!qIsNaN(ten));
QCOMPARE(qFpClassify(ten), FP_NORMAL);
// digits in the mantissa, including the implicit 1 before the binary dot at its left:
QVERIFY(qfloat16(1 << (Bounds::digits - 1)) + one > qfloat16(1 << (Bounds::digits - 1)));
@ -436,12 +493,12 @@ void tst_qfloat16::limits()
// How many digits are significant ? (Casts avoid linker errors ...)
QCOMPARE(int(Bounds::digits10), 3); // 9.79e-4 has enough sigificant digits:
qfloat16 below(9.785e-4f), above(9.794e-4f);
#if 0 // Sadly, the QEMU x-compile for arm64 "optimises" comparisons:
const bool overOptimised = false;
#if 0 // Sadly, the QEMU x-compile for arm64 "optimizes" comparisons:
const bool overOptimized = false;
#else
const bool overOptimised = (below != above);
if (overOptimised)
QEXPECT_FAIL("", "Over-optimised on QEMU", Continue);
const bool overOptimized = (below != above);
if (overOptimized)
QEXPECT_FAIL("", "Over-optimized on ARM", Continue);
#endif // (but it did, so should, pass everywhere else, confirming digits10 is indeed 3).
QVERIFY(below == above);
QCOMPARE(int(Bounds::max_digits10), 5); // we need 5 to distinguish these two:
@ -450,62 +507,23 @@ void tst_qfloat16::limits()
// Actual limiting values of the type:
const qfloat16 rose(one + Bounds::epsilon());
QVERIFY(rose > one);
if (overOptimised)
QEXPECT_FAIL("", "Over-optimised on QEMU", Continue);
if (overOptimized)
QEXPECT_FAIL("", "Over-optimized on ARM", Continue);
QVERIFY(one + Bounds::epsilon() / rose == one);
QVERIFY(qIsInf(Bounds::infinity()));
QVERIFY(!qIsNaN(Bounds::infinity()));
QVERIFY(!qIsFinite(Bounds::infinity()));
QCOMPARE(Bounds::infinity(), Bounds::infinity());
QCOMPARE(qFpClassify(Bounds::infinity()), FP_INFINITE);
QVERIFY(Bounds::infinity() > -Bounds::infinity());
QVERIFY(Bounds::infinity() > zero);
QVERIFY(qIsInf(-Bounds::infinity()));
QVERIFY(!qIsNaN(-Bounds::infinity()));
QVERIFY(!qIsFinite(-Bounds::infinity()));
QCOMPARE(-Bounds::infinity(), -Bounds::infinity());
QCOMPARE(qFpClassify(-Bounds::infinity()), FP_INFINITE);
QVERIFY(-Bounds::infinity() < zero);
QVERIFY(qIsNaN(Bounds::quiet_NaN()));
QVERIFY(!qIsInf(Bounds::quiet_NaN()));
QVERIFY(!qIsFinite(Bounds::quiet_NaN()));
QVERIFY(!(Bounds::quiet_NaN() == Bounds::quiet_NaN()));
QCOMPARE(Bounds::quiet_NaN(), Bounds::quiet_NaN());
QCOMPARE(qFpClassify(Bounds::quiet_NaN()), FP_NAN);
QVERIFY(Bounds::max() > zero);
QVERIFY(qIsFinite(Bounds::max()));
QVERIFY(!qIsInf(Bounds::max()));
QVERIFY(!qIsNaN(Bounds::max()));
QVERIFY(qIsInf(Bounds::max() * rose));
QCOMPARE(qFpClassify(Bounds::max()), FP_NORMAL);
QVERIFY(Bounds::lowest() < zero);
QVERIFY(qIsFinite(Bounds::lowest()));
QVERIFY(!qIsInf(Bounds::lowest()));
QVERIFY(!qIsNaN(Bounds::lowest()));
QVERIFY(qIsInf(Bounds::lowest() * rose));
QCOMPARE(qFpClassify(Bounds::lowest()), FP_NORMAL);
QVERIFY(Bounds::min() > zero);
QVERIFY(Bounds::min().isNormal());
QVERIFY(!(Bounds::min() / rose).isNormal());
QVERIFY(qIsFinite(Bounds::min()));
QVERIFY(!qIsInf(Bounds::min()));
QVERIFY(!qIsNaN(Bounds::min()));
QCOMPARE(qFpClassify(Bounds::min()), FP_NORMAL);
QVERIFY(Bounds::denorm_min() > zero);
QVERIFY(!Bounds::denorm_min().isNormal());
QVERIFY(qIsFinite(Bounds::denorm_min()));
QVERIFY(!qIsInf(Bounds::denorm_min()));
QVERIFY(!qIsNaN(Bounds::denorm_min()));
if (overOptimised)
QEXPECT_FAIL("", "Over-optimised on QEMU", Continue);
if (overOptimized)
QEXPECT_FAIL("", "Over-optimized on ARM", Continue);
QCOMPARE(Bounds::denorm_min() / rose, zero);
QCOMPARE(qFpClassify(Bounds::denorm_min()), FP_SUBNORMAL);
}
QTEST_APPLESS_MAIN(tst_qfloat16)

View File

@ -165,11 +165,13 @@ void Window::customRender()
u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
if (!d.compressedData.isEmpty()) {
QRhiTextureUploadDescription desc;
QVarLengthArray<QRhiTextureUploadEntry, 16> descEntries;
for (int i = 0; i < d.compressedData.count(); ++i) {
QRhiTextureSubresourceUploadDescription image(d.compressedData[i].constData(), d.compressedData[i].size());
desc.append({ 0, i, image });
descEntries.append({ 0, i, image });
}
QRhiTextureUploadDescription desc;
desc.setEntries(descEntries.cbegin(), descEntries.cend());
u->uploadTexture(d.tex, desc);
d.compressedData.clear();
}

View File

@ -143,10 +143,10 @@ void Window::customInit()
}
QRhiTextureRenderTargetDescription rtDesc;
QVector<QRhiColorAttachment> att;
QRhiColorAttachment att[ATTCOUNT];
for (int i = 0; i < ATTCOUNT; ++i)
att.append(QRhiColorAttachment(d.colData[i].tex));
rtDesc.setColorAttachments(att);
att[i] = QRhiColorAttachment(d.colData[i].tex);
rtDesc.setColorAttachments(att, att + ATTCOUNT);
d.rt = m_r->newTextureRenderTarget(rtDesc);
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();

View File

@ -181,16 +181,18 @@ void TexturedCubeRenderer::queueResourceUpdates(QRhiResourceUpdateBatch *resourc
if (!m_image.isNull()) {
if (MIPMAP) {
QRhiTextureUploadDescription desc;
QVarLengthArray<QRhiTextureUploadEntry, 16> descEntries;
if (!AUTOGENMIPMAP) {
// the ghetto mipmap generator...
for (int i = 0, ie = m_r->mipLevelsForSize(m_image.size()); i != ie; ++i) {
QImage image = m_image.scaled(m_r->sizeForMipLevel(i, m_image.size()));
desc.append({ 0, i, image });
descEntries.append({ 0, i, image });
}
} else {
desc.append({ 0, 0, m_image });
descEntries.append({ 0, 0, m_image });
}
QRhiTextureUploadDescription desc;
desc.setEntries(descEntries.cbegin(), descEntries.cend());
resourceUpdates->uploadTexture(m_tex, desc);
if (AUTOGENMIPMAP)
resourceUpdates->generateMips(m_tex);