jaros_p

People listening to the lecture

Code Europe 2022

Recently I attended the Code Europe 2022 conference in my home city, Warsaw. Conference was located in a rather unusual location – on the stadium. To be more precise it is called Stadion Narodowy or National Stadium. This stadium is rather new and was built in the place of an older, smaller stadium, which in fact served as a market in the first years of capitalism in Poland (https://en.wikipedia.org/wiki/Stadion_Dziesi%C4%99ciolecia). The benefit of using the stadium for the conference was a big car park underneath.

There were six stages at the conference. Four bigger and two smaller. Two of them were real conference spaces, the rest was created ad hoc. Nevertheless both types of bigger spaces served well for the needs of the conference. Smaller were in my opinion not acoustically isolated enough. Conference took only one day but there were many parallel tracks. Almost at every hour all 6 stages were in use. Tracks consisted of following topics: Cybersecurity, Data & AI, Infrastructure/DevOps, Future & Inspire, Programming Languages, Development Execution. I didn’t restrict myself to any subset of those. For each time slot I selected a few interesting positions, and decided on the spot which one to attend.

First lecture I listened to was about connecting web and native mobile apps. The author, Łukasz Beksa works for one of the banks operating in Poland. He advocated for using thin client apps on mobile devices to provide support for device specific features. This way we get the best of both worlds: support for native features of the device and the new content without having to update the application frequently.

Two talks at the conference were related to GitHub. One was given by GitHub employee, Michelle Mannering and was rather general. She was talking about impact of social skills and how they use GitHub inside GitHub company to many other tasks than versioning source code. The second talk was more interesting. Rob Bos from Xpirit was covering GitHub security features including commit signing and Dependabot (slides: https://devopsjournal.io/blog/2022/05/30/Code-Europe).

Rob Bos giving a talk about GitHub security features

One of the most interesting lectures, for me as both a coder and a common person, was the one given by a young girl from Poland: Barbara Klaudel. She was talking about using AI in medicine for tumor classification. She looked very modest and nervous at the conference but now digging in the Internet I found that she has some recognition, at least in Poland. She was chosen as “25 under 25” (https://www.mckinsey.com/pl/careers/25-under-25/finalists) by polish branches of McKinsey and Forbes. During the lecture, Barbara told us that it is possible to create a simulated CT image of an organ that looks as if the patient has been injected with contrast, even though no contrast has been administered. She also told us also that the some AI enabling diagnosis has already been approved in recent years (https://www.nature.com/articles/s41746-020-00324-0/figures/1).

Barbara Klaudel talking about AI in medicine.

The last lecture at the conference was the one I’ve been waiting for the most. It was given by the creator of C++ language: Bjarne Stroustrup. He was only person giving talk, that was accompanied by a guy in a dinosaur suit – the conference mascot. Keeping in mind his grey hair I hope he was not offended 🙂 During lecture Bjarne told us about the evolution of C++ and about the operation of the standardization committee. One of the most interesting facts was that he thinks Concepts introduced in C++20 should be part of the language from the time templates were introduced. He also promised that in C++23 standard library will be available for import as modules.

Bjarne Stroustrup, Code Europe 2022

As usual talks were accompanied by stands with companies looking for employees. When Bjarne finished his lecture they were all gone. It was late. I headed back to the stadium’s garage and returned home. See you next year!

Automatic UI validation in the Qt

During development of a larger desktop application using Qt widgets there is a need to connect many signals inside an application. There are two ways of doing this: using pointers to slots and signals or using names of slots and signals. I prefer the first one because this way I get slot/signal name validation at compile time. However in both scenarios developers can forget to connect the signal. It would be good if one could detect these kinds of errors programmatically. We will try to search for a solution.

Quick look at the methods available in QObject class reveals a method called isSignalConnected. This method exactly fits our needs. However it is marked protected and we want to use it from the outside of a class. We don’t want to promote all these push buttons or other widgets. We must use some trick to call this method. Fortunately in C++ one can get a pointer to almost everything, so using a dummy derived class we will steal a pointer to our method. Dummy class code is listed below:

class QObjectWrapper : public QObject
{
public:
    typedef bool (QObject::*FUNCTION)(const QMetaMethod &) const;
    FUNCTION GetPointer() { return &QObjectWrapper::isSignalConnected; }
};
 

After we have this class ready we can use it to call isSignalConnected on a real object. So first we create the wrapper and extract method pointer.

QObjectWrapper wrapper;
QObjectWrapper::FUNCTION isSignalConnectedFunc = wrapper.GetPointer();

Using this pointer we can call the method. Of course we need an argument to pass it to the function. We use QMetaMethod::fromSignal to prepare the argument. Then we can pass it to isSignalConnected. You can see it on the following code snippet:

const QMetaMethod signal = QMetaMethod::fromSignal(&QPushButton::clicked);
bool result = (object->*isSignalConnectedFunc)(signal);

With the result from that call we can do whatever we want, for example print a warning to console.

Now we will make use of our code on a larger scale. We will test signals of QPushButton and QAction and use templates to generate the code. We will create two template functions which will provide suitable signals for a specific class.

template<typename T>
QVector<const QMetaMethod*> GetSignals();

template<>
QVector<const QMetaMethod*> GetSignals<QPushButton>()
{
    static const QMetaMethod signal1 = QMetaMethod::fromSignal(&QPushButton::clicked);
    static const QMetaMethod signal2 = QMetaMethod::fromSignal(&QPushButton::toggled);
        return {&signal1, &signal2};
}

template<>
QVector<const QMetaMethod*> GetSignals<QAction>()
{
    static const QMetaMethod signal1 = QMetaMethod::fromSignal(&QAction::triggered);
    return {&signal1};
}

Then we create a core function of our testing solution. It will use a list of objects as an input. You can notice that only one connected signal will satisfy our test for a given object. We assume that if a push button uses the toggled signal, the other signal does not need to be used.

void CheckSignals(const QList<T*>& objects)
{
    QObjectWrapper wrapper;
    QObjectWrapper::FUNCTION isSignalConnectedFunc = wrapper.GetPointer();

    QVector<const QMetaMethod*> signalArr = GetSignals<T>();
    for(QObject* object : objects)
    {
        bool res = false;
        for(auto sig : signalArr)
        {
            if( (object->*isSignalConnectedFunc)(*sig) )
            {
                res = true;
                break;
            }
        }

        if(!res)
        {
            // do some logging
        }
    }
}

Finally we can call our tests:

template<typename T>
void CheckSignals(QObject* parent)
{
    CheckSignals<T>( parent->findChildren<T*>() );
}

void RunTests(QWidget* parent)
{
    CheckSignals<QPushButton>( parent );
    CheckSignals<QAction>( parent );
}

To test every window we can call the proceeding function inside their constructors and using similar templates we can also test other parts of the UI, eg. missing tooltips. I hope this solution will help you track problems in your UI before manual tests.

Programming notebooks and online IDEs

Today many tools exist to write and test some code without actually opening any traditional programming IDE or even simple text editor. Many of these tools are in fact web applications. I decided to sum up some of them:

Jupyter Lab, jupyter.org – Powerful notebook with support for many languages. Although Python is the default, built-in programming language, users can install support for many other languages (kernels). Demo is available online. Local instances can be installed with pip or conda. Supported programing languages: Python (built-in), C, C++, R, Ruby, SQL, etc (see https://github.com/jupyter/jupyter/wiki/Jupyter-kernels)

Colaboratory, colab.research.google.com – google’s Python notebook with access to GPUs and TPUs. Based on the Jupyter project. Notebooks can be saved to google drive and shared.

CodeSandbox, codesandbox.io – full blown online IDE focused on web development. It offers creating, testing, and deploying apps written in HTML, JavaScript, TypeScript, Node.JS together with popular frameworks and libraries.

Franchise, franchise.cloud – web SQL notebook with support for many different database backends. Users can easily display data as charts, maps and export results to several file types. Franchise is an open source initiative with a repository hosted on GitHub.

SQL Notebook, sqlnotebook.com – desktop SQL notebook for Windows. Can connect to external databases as well as to built-in SQLite db. Data can be imported and exported.

CodeChef , www.codechef.com/ide – CodeChef site is all about programming challenges. Their simple “IDE” support a few dozen languages including C#, Java, Rust or even the NASM assembler.

OnlineGDB, www.onlinegdb.com – online compiler with debugging. Many languages supported: C++, Java, Objective C, C#, SQL, etc. Code can be shared and stored in an online account.

C++ shell, cpp.sh – simple online c++ compiler with support for capturing input. Rather old, currently up to C++14. GCC is used underneath.

ShaderToy, www.shadertoy.com – show me your shader site. Popular among game developers. Do not launch this page on weak machines: the main page often features heavy shaders. Supported programming language: GLSL (only fragment shader, and custom “sound shader”).

Automatic translation reloading in the Qt

Professional desktop applications require translations. The Qt framework has great support for translation like special CMake functions and Qt Linguist tool. Despite this fact, after translation is updated in the Qt Linguist, the person who is editing the translation must take some steps to view results of his/her work in the application. Below I will demonstrate how to make the work of the translator easier.

First of all we want to detect file changes. Qt has a special class called QFileSystemWatcher for doing this kind of job. We pass files (or directories) we want to monitor to the constructor of QFileSystemWatcher. Then we set the handler.

#include <QFileSystemWatcher>
...
#define COMMON_DIR "../common/"
#define SOURCE_DIR "../../src/myapp/"
...
// Some place of application initialization, in my case it was
// constructor of CApplication (derived form QApllication)
...
QStringList paths =
{
   COMMON_DIR "myapp_en_US.qm",
   COMMON_DIR "myapp_pl_PL.qm",
   SOURCE_DIR "myapp_en_US.ts",
   SOURCE_DIR "myapp_pl_PL.ts",
};

m_watcher = new QFileSystemWatcher(paths, m_mainWindow);
    connect(m_watcher, SIGNAL(fileChanged(QString)), m_mainWindow, SLOT( HandleFileChanged(QString) ));

After the file system watcher is in place we must write our handler. Below you can see the contents of this method.

void CMainWindow::HandleFileChanged(const QString& filename )
{
    if(filename.endsWith(".qm"))
    {
        //code for loading the translation
    }
    else if(filename.endsWith(".ts"))
    {
        QProcess cmdProcess;
        cmdProcess.setWorkingDirectory("../..");
        cmdProcess.start("cmd.exe", {"/c release_translations.bat"});
        cmdProcess.waitForFinished(-1);
    }
}

As you can see after *.ts file is modified we start a batch file. This batch file calls lrelease executable, which converts xml based ts files into binary qm files. Normally this step is done during the build. Below you can see the contents of release_translations.bat file.

SET PATH=%PATH%;C:\Qt\5.15.2\msvc2019_64\bin

lrelease src\myapp\myapp_en_US.ts -qm bin\common\myapp_en_US.qm
lrelease src\myapp\myapp_pl_PL.ts -qm bin\common\myapp_pl_PL.qm

As an alternative to using batch file one could call lrelease application directly from Qt application. In my case I decided to use batch file as proxy, where I can modify lrelease settings, without modifying the code.

After code is up and running user can just press Ctrl+S in the Qt Linguist and the new translation will jump right into the application he/she is working on.

Counting memory in UE4

UE4 has many stats and memory counting mechanisms with great tools to display it. Unfortunately useful tools, like Statistics window or Size Map, display not really useful data. Major problem is that in-editor memory is reported and not memory for a cooked game. Another problem is that even this memory is not always properly counted. Under the hood UE4 has two memory size counting mechanisms. One is serialization based and uses FArchiveCountMem serializer. Another is based on calling a special GetResourceSizeEx virtual function defined in UObject. You can see both used in functions like UEngine::HandleListStaticMeshesCommand. Digging deeper you can see that in fact GetResourceSizeEx internally uses FArchiveCountMem to count memory of a current object.

During my work on UE4 based games (Evil West and Shadow Warrior 3) counting memory properly was a top priority. Therefore I modified memory counting mechanisms to count memory for specific cooked platforms. Using this corrected counting mechanism I was able to create easy to use level streaming budgets (displayed in the viewport) and assets’ budgets based on Data Validation interface (https://docs.unrealengine.com/en-US/ProgrammingAndScripting/ProgrammingWithCPP/Assets/DataValidation/index.html).

Creating GI Multiplier: useful custom output material node

Our team was creating cutscenes for the game and they had problems setting up proper lighting. We generally use stationary lights, because we want precomputed GI and shadows for the game. Unfortunately the cutscene team was unable to get rid of GI on characters, without modifying lighting on the environment. Custom code was needed. At first I thought I would have to add another output to the main material node, but while looking inside the code I realised that I can create a custom output node. I used UMaterialExpressionBentNormalCustomOutput as my template. Work on C++ side consisted of creating the node and registering it in FMaterialAttributeDefinitionMap::InitializeAttributeMap.

AddCustomAttribute(FGuid(0x2E5E2BF3, 0x646F4531, 0x8E36B5C0, 0xA8055AFB), "GIMultiplier", "GIMultiplier", MCT_Float1, FVector4(1, 0, 0, 0));

The engine created a shader define and function for me. On HLSL side I just used them in GetPrecomputedIndirectLightingAndSkyLight function in BasePassPixelShader.ush

#if NUM_MATERIAL_OUTPUTS_GIMULTIPLIER > 0
  float GIMultiplier = saturate(GIMultiplier0(MaterialParameters));
  OutDiffuseLighting *= GIMultiplier;
  OutSubsurfaceLighting *= GIMultiplier;
#endif

When my node was ready I placed it together with a parameter from Material Parameter Collection in base materials for characters. Now our cutscene lighting artist is able to use it inside a sequencer to scale GI to his wish.

Debugging texture streaming in UE4

Today I was working on a problem with impostor texture streaming in UE4. I was not satisfied with the possibility of disabling the streaming for those textures, because they are quite large. I also found out that generating material streaming information does not work properly, because it reads GameThreadShaderMap that, as the comment states, is loaded from cooked assets and not available in the editor. This is a topic for further investigation, though. Meanwhile my solution was to modify FStreamingTexture::GetExtraBoost function, adding a branch for impostor texture groups with a boost value of 16.

I found some useful console variables and a command during the process. Here they are:

r.Streaming.DropMips 2 - disables mip map caching, not needed mip levels will be unstreamed immediately

r.Streaming.FullyLoadUsedTextures 1 – forces loading of an entire mip chain

r.Streaming.Boost <value> - global wanted texture size multiplier

InvestigateTexture <texture name> - displays streaming information  for a texture in a console window, most notably the highest loaded mip  level 

Hello again!

Five years ago I decided that in the age of Twitter, SlideShare and DropBox my own page is not needed any more. Now jpleskot.info is back in the form of a blog.