Skip to content

**** ****

for my bad memories..

normal quantization

2023-03-27


opengl 에서 gpu로의 데이타 전송을 최소화 하는것이 성능상 이점이 있음.
특히 표면 노말의 경우는 x, y, z 3개의 float을 넘겨주는데.. 총 12바이트를 소요함.
이를 하나의 signed int (10.10.10.2) 로 표현하면 4바이트로 줄어듬.
비록 약간의 데이타 손실은 있지만 opengl에서 normal 로 표면을 표한할때는 차이가 거의 없음.
아래와 같이 약간의 손실을 감수하면서 opengl의 성능을 올릴때 사용하면 좋을것임.

노말말고 다른데 적용할부분이 어디가 있을까..

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

static inline uint8_t   encode8_unorm(float    x) { return uint8_t( int (x * 255.f + 0.5f) ); }
static inline float     decode8_unorm(uint8_t  x) { return x / 255.f; }
static inline uint8_t   encode8_snorm(float    x) { return uint8_t( int (x * 127.f + (x > 0 ? 0.5f : -0.5f)) ); }
static inline float     decode8_snorm(uint8_t  x) { float f = x / 127.f; return f <= -1 ? -1.f : (f >= 1 ? 1.f : f); }
static inline uint8_t   encode8_snorm_gl2(float   x) { return uint8_t( int (x * 127.5f + (x >= 0.f ? 0.f : -1.f)) ); }
static inline float     decode8_snorm_gl2(uint8_t x) { return (2*x + 1) / 255.f; }
static inline uint16_t  encode10_unorm(float    x) { return uint16_t( int (x * 1023.f + 0.5f) ); }
static inline float     decode10_unorm(uint16_t x) { return x / 1023.f; }
static inline uint16_t  encode10_snorm(float    x) { return encode10_unorm( ( x + 1 ) * 0.5f ); }
static inline float     decode10_snorm(uint16_t x) { return ( decode10_unorm( x ) * 2 ) - 1 ; }

int main()
{
	float x, y, z;
	x = 0.001;
	y = -0.002;
	z = 0.005;

	int16_t xx = encode10_snorm(x);
	int16_t yy = encode10_snorm(y);
	int16_t zz = encode10_snorm(z);

	uint32_t v = uint32_t( (2<<30) | (encode10_snorm(x)<<20) | (encode10_snorm(y)<<10) | (encode10_snorm(z)<<0));

	float nx = decode10_snorm( ( v >> 20 ) & 0x3FF );
	float ny = decode10_snorm( ( v >> 10 ) & 0x3FF );
	float nz = decode10_snorm( ( v >>  0 ) & 0x3FF );
	printf("%f %f %f\n", x, y, z);
	printf("%f %f %f\n", nx, ny, nz);

	printf("%d %d\n", sizeof(float)*3, sizeof(uint32_t));
}
0.001000 -0.002000 0.005000
0.000978 -0.002933 0.004888
12 4

vector 압축..

2023-03-21


진행중인 프로그램의 변수들을 파일로 저장하는 기능을 구현중인데..
vector의 크기가 너무 크다보니.. 이걸 파일로 그냥 저장하면 용량이.. 너무 커짐..
그래서 시간 대비 압축 효율이 좋으면 저장된 파일도 작으니.. 좋고해서..
테스트해봄.. 7z의 lzma 알고리즘 사용.

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <fstream> 
#include <chrono>

#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/lzma.hpp>
#include <boost/lambda/lambda.hpp>


#define DATA_COUNT 3000000
#define COMP_LEVEL 1 // compress level 9 - best size

template<typename T>
size_t vectorsizeof(const typename std::vector<T>& vec)
{
    return sizeof(T) * vec.size();
}

int main()
{
	std::chrono::system_clock::time_point start;
	std::chrono::duration<double> sec;
   
	// ------------------
	std::vector<unsigned int> vec;
	for(int i=0; i<DATA_COUNT; i++)
		vec.push_back(i);


	// -----------------------------------------------
	{
		std::cout << "NORMAL MODE" << std::endl;

		start = std::chrono::system_clock::now();
		FILE *fp=fopen("a.dat", "wb");
		fwrite(vec.data(), sizeof(decltype(vec)::value_type), vec.size(), fp);
		fclose(fp);
		sec = std::chrono::system_clock::now() - start;
		std::cout << "#2 Time passed (sec) : " << sec.count() << " seconds" << std::endl;

		// ------------------
		start = std::chrono::system_clock::now();
		std::vector<unsigned int> vec2;
		vec2.resize(DATA_COUNT);
		FILE *fp2 = fopen("a.dat", "rb");
		fread(vec2.data(), sizeof(decltype(vec2)::value_type), DATA_COUNT, fp2);
		fclose(fp2);
		sec = std::chrono::system_clock::now() - start;
		std::cout << "#3 Time passed (sec) : " << sec.count() << " seconds" << std::endl;

		std::cout << vec2[0] << "\n";
		std::cout << vec2[DATA_COUNT-1] << "\n";

	}

	// -----------------------------------------------
	{
		std::cout << "COMPRESSION MODE" << std::endl;

		start = std::chrono::system_clock::now();
		std::stringstream compressed;
		boost::iostreams::filtering_streambuf<boost::iostreams::input> out;
		out.push(boost::iostreams::lzma_compressor(boost::iostreams::lzma_params(COMP_LEVEL)));
		out.push(boost::iostreams::array_source(reinterpret_cast<const char*>(vec.data()), vectorsizeof(vec)));
		boost::iostreams::copy(out, compressed);

		const std::string tmp = compressed.str();
		const char* cstr = tmp.c_str();

		FILE *fp=fopen("b.dat", "wb");
		fwrite(cstr, 1, tmp.size(), fp);
		fclose(fp);

		sec = std::chrono::system_clock::now() - start;
		std::cout << "#2 Time passed (sec) : " << sec.count() << " seconds" << std::endl;


		start = std::chrono::system_clock::now();

		std::ifstream _compressed("b.dat", std::ios::binary);
		std::stringstream decompressed;

		boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
		in.push(boost::iostreams::lzma_decompressor());
		in.push(_compressed);
		boost::iostreams::copy(in, decompressed);

		std::vector<unsigned int> vec2;
		vec2.resize(DATA_COUNT);
		memcpy(vec2.data(), decompressed.str().c_str(), vectorsizeof(vec2));

		sec = std::chrono::system_clock::now() - start;
		std::cout << "#3 Time passed (sec) : " << sec.count() << " seconds" << std::endl;

		std::cout << vec2[0] << "\n";
		std::cout << vec2[DATA_COUNT-1] << "\n";
	}

	return 0;
}

raw 데이타 저장/ 압축후 저장

[MIN@DESKTOP-RSH0QT3 xx]$ g++ -I. a.cpp -lboost_iostreams-mt
[MIN@DESKTOP-RSH0QT3 xx]$ ./a
NORMAL MODE
#2 Time passed (sec) : 0.0039529 seconds
#3 Time passed (sec) : 0.0068222 seconds
0
2999999
COMPRESSION MODE
#2 Time passed (sec) : 0.461739 seconds
#3 Time passed (sec) : 0.111355 seconds
0
2999999

압축된 벡터를 저장하고 있는 파일 사이즈..
약 55배의 시간이 더 드나.. 50배의 용량을 줄일수있음..

-rw-r--r-- 1 MIN 없음  12M Mar 21 13:23 a.dat
-rw-r--r-- 1 MIN 없음 232K Mar 21 13:23 b.dat

수치상으로는 시간이 55배 더드는데.. 사람이 느끼기엔.. 그닥 큰 시간은 아니라..
이정도로 만족..


bit operation

2023-03-09


프로그램의 메모리 점유율을 줄이는 목적으로..
변수 하나로 수개에서 수십개의 상태를 저장하기 위해서..
bit operation이 필요함..
아래는 예..

#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

void main()
{
	char v = 0;
	BIT_SET(v,0);
	printf("%d\n", v);
	BIT_SET(v,1);
	printf("%d\n", v);
	printf("%d\n", BIT_CHECK(v,0));
	printf("%d\n", BIT_CHECK(v,2));
	BIT_CLEAR(v,1);
	printf("%d\n", v);
}

OpenCASCADE representation surface cross line

2022-12-09


제목이 거창한데.. 제목을 뭐라 해야할지.. ㅠ

OpenCASCADE의 edge 정보를 기반으로 wire 프레임을 보여주는 기능을 구현중인데..
이 wire 프레임 만으로는 형상에 대한 정보를 얻기 어려울때가 있음.

아래와 같은 형상이 있을때..

wire 프레임으로 표현하면 이런데..

cone 형상이나 cylinder 형상 같은건 wire 프레임 보기 모드에서는 쪼메 형상 판단이 안됨..
그래서 non plane surface의 가운데를 가로지는 wire를 추가적으로 보여주도록 기능 구현 해봤음.

단점은.. 이 cross 라인의 정보를 구하기 위해서는 시간이 쪼메 더 걸린다는 단점이…
그래서 할수없이.. 옵션으로 두어야 겠음.


OpenCASCADE 연동 진행 상황..

2022-12-07


OpenCASCADE는 뷰어를 따로 제공해 주고 있지만
캐드 커널이 바뀔경우를 대비해 테셀레이션 기능만 쓰고 OpenGL에 직접 그려서 구현중임.

아래는 RC카중 가성비로 짱인 중국제 Buggy 모델의 서스편션 부분에 대한 step 파일이다.

일단 테셀레이션도 어느정도 트라이 과정을 거친후 코드를 작성해서 잘 표현해 주고 있다.
현재 작성 중인 프로그램은 GPU 기능을 쓰도록 되어 있어서 상당히 빠른 디스플레이를 보여주고 있다.

아래는 일부를 확대한 화면인데 edge도 잘 보여줌.
이 edge도 상당히 문제가 많았는데 어떻게 보여줘야 z-fighting 이 일어나지 않고 잘 보여줄지..
상당히 고민하다.. 어느정도 타협점을 찾은 상태이다.

이왕 하는거 당연하지만 edge 피킹 기능을 추가함.

다음 surface 피킹 기능을 구현한 화면.

다음 솔리드 피키 기능을 구현한 화면.

이제 속도를 좀더 내서 OpenCASCADE의 BREP API를 tcl 커맨드로 바인딩 해봐야겠음..


날씨 정보 rss

2022-05-17


아래에서 시/도 코드를 얻고…
http://www.kma.go.kr/DFSROOT/POINT/DATA/top.json.txt

[{"code":"11","value":"서울특별시"},{"code":"26","value":"부산광역시"},{"code":"27","value":"대구광역시"},{"code":"28","value":"인천광역시"},{"code":"29","value":"광주광역시"},{"code":"30","value":"대전광역시"},{"code":"31","value":"울산광역시"},{"code":"41","value":"경기도"},{"code":"42","value":"강원도"},{"code":"43","value":"충청북도"},{"code":"44","value":"충청남도"},{"code":"45","value":"전라북도"},{"code":"46","value":"전라남도"},{"code":"47","value":"경상북도"},{"code":"48","value":"경상남도"},{"code":"50","value":"제주특별자치도"}]

아래에서 군/구 코드를 얻고.. (30은 대전)
http://www.kma.go.kr/DFSROOT/POINT/DATA/mdl.30.json.txt

[{"code":"30110","value":"동구"},{"code":"30140","value":"중구"},{"code":"30170","value":"서구"},{"code":"30200","value":"유성구"},{"code":"30230","value":"대덕구"}]

아래에서 읍/면/동 코드를 얻는다.. (30170는 서구)
http://www.kma.go.kr/DFSROOT/POINT/DATA/leaf.30170.json.txt

[{"code":"3017051000","value":"복수동","x":"67","y":"99"},{"code":"3017052000","value":"도마1동","x":"67","y":"100"},{"code":"3017053000","value":"도마2동","x":"67","y":"100"},{"code":"3017053500","value":"정림동","x":"67","y":"99"},{"code":"3017054000","value":"변동","x":"67","y":"100"},{"code":"3017055000","value":"용문동","x":"67","y":"100"},{"code":"3017055500","value":"탄방동","x":"67","y":"100"},{"code":"3017056000","value":"괴정동","x":"67","y":"100"},{"code":"3017057000","value":"가장동","x":"67","y":"100"},{"code":"3017057500","value":"내동","x":"67","y":"100"},{"code":"3017058100","value":"갈마1동","x":"67","y":"100"},{"code":"3017058200","value":"갈마2동","x":"67","y":"100"},{"code":"3017058600","value":"월평1동","x":"67","y":"100"},{"code":"3017058700","value":"월평2동","x":"67","y":"101"},{"code":"3017058800","value":"월평3동","x":"67","y":"101"},{"code":"3017059000","value":"가수원동","x":"67","y":"99"},{"code":"3017059600","value":"관저1동","x":"66","y":"99"},{"code":"3017059700","value":"관저2동","x":"66","y":"99"},{"code":"3017060000","value":"기성동","x":"67","y":"98"},{"code":"3017061000","value":"삼천동","x":"68","y":"100"},{"code":"3017063000","value":"둔산1동","x":"67","y":"100"},{"code":"3017064000","value":"둔산2동","x":"67","y":"100"},{"code":"3017065000","value":"만년동","x":"67","y":"101"}]

아래에서 최종 날씨 정보를 얻는다. (둔산2동)
http://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=3017064000

각 노드 정보는 여기서 볼것..
https://www.weather.go.kr/w/resources/pdf/dongnaeforecast_rss.pdf


gcc 병렬 컴파일

2022-05-17


make가 병렬 컴파일이 지원되는 줄 오늘에야 알았다..
흑.. 내 시간…

-j 옵션없이 컴파일..

$ make
....
real    3m5.355s
user    0m0.980s
sys     0m1.836s

프로세스 4개 부여하여 컴파일..

$ make -j4
....
real    1m0.297s
user    0m0.979s
sys     0m2.233s

프로세스 8개 부여하여 컴파일..

$ make -j8
....
real    0m41.000s
user    0m1.010s
sys     0m2.417s

프로세스 16개 부여하여 컴파일..

$ make -j16
....
real    0m36.750s
user    0m1.131s
sys     0m3.057s

기본 환경변수로 등록도 가능.

$ export MAKEFLAGS=-j16
$ make

아래 참고..
출처: http://www.digipine.com/index.php?mid=clan&document_srl=598
make의 -j 옵션은 한번에 수행할 수 있는 명령(Job)을 지정하는 병렬 실행(Parallel Execution) 옵션입니다.
예를 들어 CPU코어가 4개라면 한번에 수행할 수 있는 명령을 -j4로 지정하여 프로세스가 4개 생성되어 병렬로 컴파일합니다.
명령수는 프레스의 수로 정의됩니다.
이렇게하여 소스를 컴파일시 4개 CPU의 자원을 최대한 활용하게 되는 것입니다. top 명령으로 확인하면 바로 알수있습니다.
make의 개발자에 의하면 CPU 총 코어 갯수에서 20%의 코어 갯수를 추가하는 것이 속도가 가장 좋게 작업되었다고 합니다.
코어가 8개이면 8 + 1.6 (8 의 20%) 이므로 -j 10 정도가 적당할 것 같습니다.
일반적으로 커널 소스 빌드시에는
간단히 > make -j`cat /proc/cpuinfo | grep cores | wc -l` kernel.img 하는 것이 좋습니다.
아래는 최상의 속도를 내는 스크립트 예제 입니다.

#!/bin/bash
cpunum='cat /proc/cpuinfo | grep cores | wc -l`
let bestnum=$cpunum+$(printf %.0f`echo "$cpunum*0.2"|bc`)
make -j`echo $bestnum` kernel.img