Archiwum | Uncategorized RSS for this section

Blog moved to github.io

https://arekmd.github.io/

Reklamy

QuickTip – QTCreator – debugging signed android application

While working on Android application using QTCreator, i have got strange debugger error.

Application was compiled, signed, and properly deployed on my test Android phone. But debugger did not want to start.

Logs shows repeated lines:

I/Qt JAVA (23201): DEBUGGER: go to sleep
I/Qt JAVA (23201): DEBUGGER: Waiting for debug socket connect

Google wasn’t helpful. But after long hours of tests, i found a solution.

Looks like QTCreator handle Android’s manifest „debuggable” flag by itself. But flag is not generated, when application is signed.

If you want to debug signed application. Add android:debuggable=”true” flag into manifest’s tag.

Runtime texture atlas generation

Texture atlas – what is it

Texture atlas is a image containing collections of sub-images. Each of these sub-images representing a texture.

Application can use multiple texture atlases.

Why

Rendering a texture require setting it as active. This operation is time consuming. Since atlas is a single texture containing many of different sub-textures, active texture switches are much rarer. Renderer use region of texture atlas to render concrete texture.

Reducing OpenGL state changes, can give significant performance gains.

Runtime texture atlas generation

Texture atlas can be pre-generated. There are many tools to do it. Usually tool generates also some meta-data file containing coordinates of each sub-texture inside atlas.

Texture atlas can also be generated runtime. Images representing a texture are copied into single large, in-memory image.

Pros of runtime generation

Simplified development process

Images can be added to project (or resource file) immediately. In case of pre-generated texture atlas, image has to be merged in to atlas each time artist submit one.

Image size

Texture size has to be equal to power of two. If not, render process may suffer performance hit. Some OpenGL implementation do not load image at all. Image placed in texture atlas can be any size (Smaller then atlas size).

Optimizations

There are hundreds of different devices. Some of them differ in screen resolution, maximum texture size, etc. Building texture atlas during runtime gives opportunity to optimize textures. On low-end devices, that do not use high-resolution textures, images can be down-sized. So textures would better suited for the particular machine.

Size of texture atlas can be adapted for the machine. OpenGL function glGetIntegerv(GL_MAX_TEXTURE_SIZE …) returns maximum size of the texture. Value can be used to set atlas dimension.

Cons

Creation takes time

Obviously all images merged into texture has to be loaded into memory, and printed into single large one. Additional image resizing also takes extra time.

Implementation description

Runtime texture atlas generation process consists of two phases: * Calculating placement of each sub-texture. * „Painting” sub-textures on atlas texture.

In this post, I will describe only first phase.

Canvas

Texture atlas texture must have dimensions. These values can be based on machine’s GL_MAX_TEXTURE_SIZE. The safest are 1024×1024. All devices should support these.

The canvas on which sub-textures are going to be placed is represented by rectangle:

{ x= 0, y = 0, width = 1024, hight = 1024}

Lets define a list, collection or whatever, which will contain rectangles of free space on canvas. Initially collection will contain single rectangle. The one describing whole canvas.

Divide and conquer

The canvas is just a big rectangle. Image which is going to be added to atlas is smaller rectangle. Smaller subtracted from bigger give surface that can be described as two rectangles:
img1
Sub-texture (green background) was added. Space left (blue rectangle, and red rectangle).

Now. The collections of canvas free space has to be changed. Rectangle which represents whole canvas has to be removed. And two new rectangles has to be add.

Sub-texture coordinates has to be stored somewhere.

Lets add another sub-texture (black background).

img3

Another two rectangles representing free space ware created (orange and white background)

Horizontal or vertical

Surface after subtraction can be divided in two ways. Horizontal or vertical.

img2

Choose one better suited for your textures.

Algorithm

As you may figure it out. Algorithm is quite simple:

  • Get your sub-texture’s dimensions.
  • Find free rectangle that fits sub-texture dimensions.
  • Subtract sub-textures’s dimensions from found rectangle. The result of the subtraction will be two rectangles
  • Store sub-texture coordinates (rectangle) in separate collection.
  • Remove found rectangle from list.
  • Add two new rectangles (result of subtraction) to list.
  • If image does not fit into texture atlas, generate new texture atlas
  • Repeat for each sub-texture

Matching

Whole process of finding fitting rectangle can be optimized by sorting the list.

Padding

Leave at least 1 pixel space between sub-textures. OpenGL does not work based on pixels, but on normalized values. That means some inaccuracies may be added, and „leaks” between textures can be visible.

Coordinates

The result of atlas generation process, should be list of rectangles – coordinates of each sub-texture. The list has to be transformed in to OpenGL texture coordinates list.

Result

example

Tips

  • Coordinate systems can be different. OpenGL’s {0,0} point lays in left, bottom corner. Qt for example, using top left.
  • Sub-texture itself can be atlas (Inception?). For example texture containing font glyphs. sub-sub-texture coordinates has to recalculated.

MongoDb i C#

W dzisiejszym wpisie postaram się przybliżyć bazę MongoDB i sposób korzystania z niej za pomocą C#. Nie widzę jakiegoś wielkiego zainteresowania tą bazą w polskiej blogosferze, a szkoda.

MongoDb jest obecnie chyba najbardziej popularną bazą danych NoSQL.
Co to jest NoSQL? Jak sama nazwa wskazuje jest to silnik który nie używa SQLa do zapytań.
Po co komu baza, która posiada jakiś autorski dialekt zapytań, i nie korzysta z dobrze znanej składni SQLa? Otóż są zastosowania w których bazy NoSQL sprawują doskonale. Wszędzie, gdzie mamy dużo (lub bardzo dużo) danych, mało (najlepiej w ogóle) relacji, warto zastanowić się nad Mongo.

Trzeba zaznaczyć, że MongoDB nie jest „silver bullet”, próba zastąpienia relacyjnej bazy danych w systemie mającym relacje między tablicami zakończy się wielkim rozczarowaniem uzyskaną wydajnością.

Tworzeniem i wsparciem silnika MongoDb zajmuje się firma 10gen. I robi to bardzo dobrze. Dostępne są drivery dla wszystkich liczących się języków programowania, driver dla .NET jest przyzwoicie napisany. Kilka miesięcy temu zostało dodane wsparcie dla Linq. Kod pozwalający na połączenie z bazą, dodanie kilku dokumentów do kolekcji, to zaledwie kilka linii.

Wspomniałem już, że Mongo nie jest bazą relacyjną. Co prawda, na upartego można próbować na piechotę łączyć dane z różnych kolekcji, ale wydajność takich operacji będzie niska.

Dane przechowywane są w formacie JSON, a dokładnie w klonie tego formatu pozwalającym na zapis danych binarnych, czyli BSON.
Dokumenty BSON są przechowywane we wspomnianych już kolekcjach. Na upartego można porównać dokument Mongo z rekordem relacyjnej bazy danych, a kolekcję z tablicą. I tu trzeba wspomnieć o rzeczy bardzo ważnej – kolekcje nie posiadają zadanego sztywnego schematu, można w nich przechowywać dowolne dokumenty BSON.

Niestety Mongo posiada także i ograniczenia. Uruchomione na 32-bitowym systemie, pozwala na przechowanie nieco ponad 2GB danych.
Jest to związane ze sposobem, w jaki dane są zapisywane w plikach bazy. Mongo mapuje plik w pamięci, a 32-bitowy system operacyjny nie jest w stanie zaadresować większych obszarów. Tak więc, jeśli baza przekroczy 2GB (cała baza, czyli dane + indeksy), mongo zakończy pracę mało przyjemnym błędem.

Jeśli komuś chodzi po głowie deployment bazy wraz z desktopowym klientem, niech się lepiej dobrze zastanowi.

Uruchomienie mongo jest bardzo proste:

mongod --dbpath "sciezka do folderu z danymi" --logpath "sciezka do folderu na logi"

aby zainstalować mongo jako usługę systemową należy dodać parametr –install do tych powyżej
Zainstalowaną usługę warto uruchomić:

net start MongoDB

Polecenia trzeba wykonywać za pomocą linii komend z uprawieniami administracyjnymi.

Jak już wspomniałem, dokument mongo nie ma ustalonego schematu. Do kolekcji można dodać dowolne dane, byle były opakowane w JSON.

Wszystko pięknie, ale czasami wygodnie jest zdefiniować sobie klasę danych, która będzie automatycznie serializowana i deserializowana, tak jak robią to nHibernate, czy Entity Framework.

Sterownik dla .NET zapewnia taką funkcjonalność.

pubic class KlasaA
{
  public ObjectId _id {get; set;}
  public List<KlasaB> ObiektyKlasyB {get; set;}
}

Jak widać, dane mogą być zagnieżdżone. KlasaA może zawierać listę obiektów klasy B

KlasaA, jako „root” powinna mieć zdefiniowane pole które będzie identyfikatorem. Najprościej jest dodać właściwość _id typu ObjectId. Ale za pomocą atrybutów można wyznaczyć praktycznie dowolne pole aby było identyfikatorem.

Atrybutów którymi można udekorować klasy i ich właściwości jest znacznie więcej, znajdują one się w przestrzeni nazw MongoDB.Bson.Serialization.Attributes. Bardzo przydatnym atrybutem jest BsonIgnoreExtraElements, dzięki któremu nie jest wyrzucany wyjątek, kiedy klasa nie zawiera pola które znajduje się dokumencie JSON.

Połączenie z bazą:

var connectionString = "mongodb://localhost";
var client = new MongoClient(connectionString);
var server = client.GetServer();
var database = server.GetDatabase("NAZWA_BAZY_DANYCH");

Baz danych możne być oczywiście kilka na serwerze.

Zdefiniuję teraz prostą encję:

public class User
{
  public ObjectId _id {get; set;}
  public string Name {get; set;}
}

Pobieramy kolekcję:

var users = database.GetCollection<User>("Users");

Dodajemy dokument do kolekcji

var user = new User() {Name="Arek"};
users.Insert(user);

Zapytanie:

var arki = users.AsQueryable().Where(u=> u == "Arek");

Niestety, na dzień dzisiejszy nie wszystkie funkcje LINQ są wspierane. Aby na
przykład pogrupować dokumenty trzeba się trochę nagimnastykować. Trzeba skorzystać z JavaScriptu. Takie zapytania to dosyć obszerny temat, postaram się je opisać w osobnym wpisie.

Jeśli pola używamy jako kryterium wyszukiwania, warto założyć na nim indeks. Poprawi to znacznie wydajność zapytań.

Robi się to bardzo łatwo:

users.EnsureIndex("Name");

To wszystko jeśli chodzi o podstawowe informacje. Mam nadzieję, że wystarczą do uruchomienia i rozpoczęcia eksperymentów z Mongo. A kolejnej notce omówię
grupowanie danych, i prace z klasę BsonDocument.

Hyper-V vs VirtualBox

Zainstalowałem Windows 8.

Po przeczytaniu kilku entuzjastycznych postów o Hyper-V uruchomiłem tą opcję w systemie.
Niestety nie miałem czasu, aby się nią pobawić. Do pracy używam na co dzień VirtualBox, którego sobie bardzo chwalę.

Zdziwiłem się „lekko”, kiedy po uruchomieniu Hyper-V VirtualBox zaczął rzucać komunikatami błędów. Jeszcze bardziej się zdziwiłem, kiedy okazało się, że wcale nie jest tak prosto wyłączyć Hyper-V.

Ale znalazłem polecenie które załatwiło sprawę (cmd.exe uruchom jako administrator):

dism.exe /Online /Disable-Feature:Microsoft-Hyper-V /All

PS. Hyper-V „kradnie” sprzętowe wsparcie procesora dla wirtualizacji. VirtualBox próbuje uruchamiać wirtualne maszyny z takim wsparciem, nie znajduje go i sypie błędami.

.NET i FTP.

Pobieranie pliku z serwera FTP:

var request = FtpWebRequest.Create(ftpServer) as FtpWebRequest;
setCredentials(request);
FtpWebResponse response = (FtpWebResponse)request.GetResponse();

using (var destStream = new System.IO.BufferedStream(System.IO.File.Create(destinationFile)))
using (var srcStream = new System.IO.BufferedStream(response.GetResponseStream()))
{
  // Odczyt z srcStream i zapis do destStream
}

BufferedStream zapewnia elegancki sposób pobierania i zapisywania danych. Wewnątrz using, można operować na pojedynczych bajtach. Jednak odczyt z serwera FTP i zapis do pliku odbywa się w blokach danych – właśnie dzięki BufferedStream.

A co jeśli chcielibyśmy pokazać zaawansowanie operacji pobierania? Do tego potrzebujemy wielkości pliku. Niestety każda próba pobrania wielkości danych ze strumienia response.GetReponseStream() kończy się mało przyjemnym wyjątkiem. Szczęśliwie jest inna droga:

FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpServer);
request.Method = WebRequestMethods.Ftp.GetFileSize;
setCredentials(request);
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
long fileSize = response.ContentLength;

Systemy kontroli wersji

W mojej pierwszej pracy, gdzie byłem zatrudniony na stanowisku programisty, pracowałem w niewielkim zespole nad desktopową aplikacją tworzoną za pomocą Delphi 1 (Nie, nie jestem aż taki stary :) firma rozwijała swój leciwy produkt, mimo, że na rynku od wielu lat dostępne były 32-bitowe wersje Delphi).

Byłem najmłodszym programistą w zespole, moim najbardziej niewdzięcznym zadaniem było łączenie wersji kodu od różnych developerów w jedną – aktualną i obowiązującą całość. Co kilka dni otrzymywałem spakowane ZIPem pliki źródłowe, które następnie pracowicie porównywałem, i generowałem scaloną wersję. Taka operacja trwała kilka godzin, i jak łatwo sie domyślić – o błąd było bardzo łatwo.

Jakiś czas później wyjechałem do Wielkiej Brytanii, i rozpocząłem pracę w małym studiu developerskim. Tu poznałem Subversion. Natychmiast się zakochałem w tym środowisku. To co kiedyś pracowicie łączyłem „na piechotę” – robiło się samo, bez większych problemów.

Ostatnio odkryłem GITa, rozproszone repozytoria, lokalne commity, no i przede wszystkim prosty i bardzo szybki branching –  wszystkie te funkcje bardzo pomagają podczas programowania. Dzisiaj już nie wyobrażam sobie kodowania bez wsparcia SCM.

Jeśli ostatnie 10 lat rozwoju narzędzi developerskich przesiedziałeś pod kamieniem, i podczas codzinej pracy nad kodem nie korzystasz z żadnego systemu kontoli wersji, to czas najwyższy wyjść i  zacząć korzystać.

Już niebawem kolejny wpis – GIT w praktyce