Justin's Code Haus
Ramblings of a compiler engineer

Author: Justin Holewinski

2012 and still no multi-monitor lovin'...

For the past week, I've been giving Windows 8 a serious try.  I've dabbled with the various preview versions, but now it's time to try the released version.  I've read the reviews, saw the mixed reactions to the new "Modern UI", and decided to see for myself how I feel about it. Generally speaking, I don't mind the new interface.  My usual method of launching a program was pressing the Win key, typing the first few letters of the application name, and then hitting Enter.  Still works in the Start Screen.  Start Menu vs. Start Screen, it doesn't bother me.  Except for one little detail...

... the complete lack of multi-monitor support in the Modern UI!

Simply put, there can only be one full-screen Modern UI app displayed at a time.  So if I open, say, Mail on one screen and attempt to open Maps on another screen, the screen showing Mail will go back to the desktop.  To make matters worse, the Start Screen counts as a Modern UI app in this regard.  If I have Mail open on one screen and try to open the Start Screen on another screen to launch an application, the screen showing Mail goes back to the desktop and I have to alt+tab back to it.  Talk about a disruption in my workflow!

Did I mention my main desktop is triple-head?  It comes in handy for coding.  All of the Windows 8 reviews I read talk about the pros and cons of the new Modern UI, but I rarely see any reference to the complete lack of multi-monitor support in it.  I'm not sure if this is just a feature oversight, or a more fundamental limitation in the model used for WinRT apps.   Is there a fundamental technical limitation in the operating system that prevents multiple Modern UI apps to be visible at the same time? If the Modern UI is the future, I sure hope they get this fixed ASAP!  For now, it's easy enough to just avoid Modern UI apps altogether, but what happens in the future when I find an app I want to keep open on one of my screens?

Perhaps more disturbing, this is becoming a trend in mainstream operating systems.  While Windows 8 has this limitation for Modern UI apps, full-screen apps in Mac OS X are not really any better.  If I full-screen an application in a multi-monitor configuration, I get that app on one monitor and a pretty background on all others.  I would much rather have the ability to put multiple apps in full-screen mode side-by-side, or perhaps have the option to have a full-screen app next to a desktop workspace on another monitor.  At least I can have multiple workspaces, though.

Perhaps I'm just too adjusted to my Linux multi-monitor configurations...

Posted Tue 06 November 2012 by Justin Holewinski in General (Windows, Desktop)

AMD APP: Getting Device Assembly

Sometimes it is useful to look at the intermediate and assembly code for GPU programs.  This can lead to some interesting performance insights, especially for compiler writers.  Unfortunately, the AMD APP SDK is a bit limited on Linux, and the AMD APP KernelAnalyzer, which conveniently dumps the AMDIL and Device ISA for an OpenCL kernel, is not available on Linux.  However, digging through the AMD APP OpenCL Programming Guide, one finds an environment variable that can be used for the same purpose: GPU_DUMP_DEVICE_KERNEL.

According to the programming guide, this environment variable can take one of three values:

1 Save intermediate IL files in local directory.
2 Disassemble ISA file and save in local directory.
3 Save both the IL and ISA files in local directory.

Therefore, if you run your OpenCL program with:

$ GPU_DUMP_DEVICE_KERNEL=3 ./my-program

You will get two files in your local directory: [kernel-name]_[device-name].il and [kernel-name]_[device-name].isa, which contain AMDIL and Device ISA disassembly, respectively.

Posted Thu 09 February 2012 by Justin Holewinski in Programming (GPU, OpenCL, AMD)

Direct3D 11 with Qt 4

(If you're in a hurry, the full source can be found on my BitBucket account)

When it comes to GUI frameworks for C++, it's very hard to beat Qt.  It's modular, easy to use, and available on practically any desktop system (and even a few mobile systems).  The MOC'ing can get a bit annoying, but IDE and command-line support is very mature at this point.  However, only OpenGL is supported currently for real-time 3D rendering. If you want to render to a Qt widget from a Direct3D 11 device, you end up having to do a lot of setup yourself.

Unfortunately, there is not a lot of information out on the internet about setting up Direct3D to play nice with Qt.  Most of the information is either out-dated, or only applies to Direct3D 9.  Lately, I've been playing around with this and I want to share my method for combining Direct3D 11 and Qt.

Screenshot

Creating a Widget

To start, we define a new widget sub-class specifically for Direct3D 11 rendering. On the Qt side, the key to eliminating flickering or UI artifacts is the paintEngine() method.  We need a way to tell Qt that we want complete control over drawing for our widget, so we can override paintEngine() in our widget definition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class D3DRenderWidget : public QWidget {
  Q_OBJECT
  Q_DISABLE_COPY(D3DRenderWidget)
public:
  D3DRenderWidget(QWidget* parent = NULL);
  virtual ~D3DRenderWidget();
  virtual QPaintEngine* paintEngine() const { return NULL; }
protected:
  virtual void resizeEvent(QResizeEvent* evt);
  virtual void paintEvent(QPaintEvent* evt);
};

(Note that for ease of viewing, all of the fields have been removed from this code snippet)

We also need to set a few attributes on our widget, as shown in the constructor:

1
2
3
4
5
6
7
8
D3DRenderWidget::D3DRenderWidget(QWidget* parent)
  : QWidget(parent) {
  setAttribute(Qt::WA_PaintOnScreen, true);
  setAttribute(Qt::WA_NativeWindow, true);

  // Create Device
  createDevice();
}

First, we tell Qt that we do not want it to do any draw buffering for us. Second, we require a native window handle for our widget. Otherwise, Qt may re-use the same native handle for multiple widgets and cause problems for our Direct3D rendering. You may have also noticed the createDevice() method call; this will be explained in a bit.

Creating the Direct3D 11 Device

Now that we have a basic widget that can support Direct3D rendering, we can initialize the Direct3D 11 device we want. This procedure is mostly identical to setting up Direct3D in a raw window. The only difference is that we must use the width(), height(), and winId() methods to return the widget size and native window handle, respectively:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
swapChainDesc_.BufferCount = 1;
swapChainDesc_.BufferDesc.Width = width();
swapChainDesc_.BufferDesc.Height = height();
swapChainDesc_.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc_.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc_.SampleDesc.Count = 4;
swapChainDesc_.SampleDesc.Quality = 0;
swapChainDesc_.Windowed = true;
swapChainDesc_.OutputWindow = winId();
swapChainDesc_.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc_.BufferDesc.RefreshRate.Denominator = 1;

Everything else remains the same... pretty easy, huh? :)

Handling Paint Events

Remember the paintEvent override from the widget class definition? We can simply implement it with a call to some rendering function:

1
2
3
void D3DRenderWidget::paintEvent(QPaintEvent* evt) {
  render();
}

Here, render() is just some arbitrary method that uses the Direct3D 11 device to render something to the primary swap chain.

Handling Resize Events

Resize events are perhaps the hardest events to handle when integrating Direct3D 11 and Qt. To resize our swap chain, we need to release all device-allocated resources, and reallocate them. The procedure I follow is:

1
2
3
4
5
6
7
8
void D3DRenderWidget::resizeEvent(QResizeEvent* evt) {
  releaseBuffers();
  swapChain_->ResizeBuffers(1, width(), height(), swapChainDesc_.BufferDesc.Format, 0);
  swapChain_->GetDesc(&swapChainDesc_);
  viewport_.Width = width();
  viewport_.Height = height();
  createBuffers();
}

We start by releasing all of the buffers we had allocated (vertex buffers, index buffers, shaders, textures, etc.). We then issue a resize request to the swap chain, resize our rendering viewport, and then recreate all of our needed buffers. In this snippet, releaseBuffers() will call Release() on all buffers, and createBuffers() will create all of the needed resources (again).

It would probably be easier to just allow the swap chain to grow and just adjust the viewport if the widget shrinks, but this method shows how to keep the swap chain the exact same size as the widget.

Conclusion

At this point, you should have a functional Direct3D 11 rendering context for a Qt widget. For brevity, I have omitted most of the Direct3D initialization code (this can be found in many places on the web).

If you want to check out the complete sample program, it is located on my BitBucket account. To build it, you need a relatively recent Qt release, the DirectX SDK, and the Qt Visual Studio Add-in.

Posted Thu 16 February 2012 by Justin Holewinski in Programming (Direct3D, Qt, Windows, C++)

i3 WM: Default Workspace Settings

I've been playing around with the i3 window manager a lot lately, and I have to say I enjoy it very much! But one default setting that tripped me up (as set by i3-config-wizard) was the key bindings for workspace switching:

bindsym $mod+1 workspace 1
bindsym $mod+2 workspace 2
bindsym $mod+3 workspace 3
# ...

This works well until you start naming your workspaces. With these bindings, you will switch to workspace name "1", not number "1". So if you rename workspace 1 to "1: Firefox" and press Mod5+1, you'll end up opening a new workspace instead of going to the "1: Firefox" workspace. Not very intuitive! Luckily, we can easily get the desired behavior with a slight tweak to the config file:

bindsym $mod+1 workspace number 1
bindsym $mod+2 workspace number 2
bindsym $mod+3 workspace number 3
# ...

Posted Thu 11 July 2013 by Justin Holewinski in General (Linux, i3)

Installing Matplotlib on OS X 10.7 with Homebrew

[edit: It looks like things have changed a bit since the release of 10.7, so your mileage may vary with this method.  This was written when 10.7 was brand new and most software was not yet updated for it.]

For those of you that do not know, Matplotlib is an excellent Python plotting library that allows you to create professional-quality plots for inclusion on web pages, Latex documents, Beamer presentations, Keynote presentations, and any other software that can import SVG, EPS, PNG, or virtually any graphic format.

However, getting matplotlib installed on Mac OS X 10.7 can be a bit tricky, especially if you are using Homebrew as your "package manager."  First off, Homebrew does not have packages for matplotlib, as well as some of its dependencies.  Additionally, the current Matplotlib release version (1.0.1 as of this post) does not compile out-of-the-box against libpng 1.5, which is included in the X11 distribution shipped with Mac OS X 10.7.

For previous versions of Mac OS X (10.6, 10.5), the usual way to install matplotlib was to install python, pkg-config, and gfortran with Homebrew, then install numpy and matplotlib through pip, ala:

$ brew install python
$ brew install gfortran
$ brew install pkg-config
$ easy_install pip
$ pip install numpy
$ pip install matplotlib

Unfortunately, as previously mentioned, all is not so easy in the world of Mac OS X 10.7, and the difficulty lies with libpng 1.5, installed with Mac OS X 10.7's version of X11. Briefly put, Matplotlib 1.0.1 is not compatible with libpng 1.5 due to a change in the API. Fortunately, the fix is already applied up-stream and will probably be a part of Matplotlib 1.0.2, or 1.1.0, or whatever the next released version is.

Until the next release, the Matplotlib sources in Git can be used. Instead of pulling the sources from the Matplotlib SourceForge site, you need to pull them from the Matplotlib GitHub site. I'm not sure if this GitHub site is "official," but is looks to be.

All that is needed is to build Matplotlib from source instead of using pip, so the installation procedure is now:

$ brew install python
$ brew install gfortran
$ brew install pkg-config
$ easy_install pip
$ pip install numpy
$ cd $HOME
$ git clone https://github.com/matplotlib/matplotlib.git
$ cd matplotlib
$ python setup.py build
$ python setup.py install

And now you're good to go! Hopefully this will become much easier with the next official release of Matplotlib.

Posted Thu 21 July 2011 by Justin Holewinski in General (Homebrew, Mac OS X, Matplotlib)