http://objccn.io/issue-9-1/
作者: wudijimao
ES3.0 中 Vertex Shader 的out变量未赋值,导致 glLinkProgram 失败
这其实是我在群里问这位大神的问题,当时是获取不到 glGetUniformLocation(program, “s_texture”); 这个东西。
版权声明:本文转自http://blog.csdn.net/huutu 转载请带上 http://www.thisisgame.com.cn
GLSL中,定义了但是 没有使用的变量会被优化掉,在之前就遇到过。
(GLSL 中 ,如果定义的 变量 没有在代码中使用到,那么不会被编译,用 glget* 是获取不到的,返回 -1 。算是被优化掉了。
http://blog.csdn.net/huutu/article/details/50322905 )
而且如果 Vertexshader 中的定义的 Out 变量未使用,被优化掉后,会引起 FragmentShader 的问题,导致 glLinkProgram 失败!
群里朋友 学习 ES3.0的时候 碰到一个问题:
编译下面的Shader 并没有出错,但是使用
- glGetUniformLocation(program, “s_texture”);
去获取 的时候,却返回了 -1 。转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Vertex Shader:
- #version 300 es
- precision mediump float;
- in vec3 inPos;
- in vec2 vTexCoord;
- out vec3 Color;
- out vec2 texCoord;
- uniform mat4 myMat;
- void main() {
- gl_Position = vec4(inPos, 1.0) * myMat;
- Color = inPos;
- }
Fragment Shader:
- #version 300 es
- precision mediump float;
- in vec3 Color;
- in vec2 texCoord;
- out vec4 fragmentColor;
- uniform sampler2D s_texture;
- void main() {
- fragmentColor = texture(s_texture, texCoord);
- }
我的第一反应是:
s_texture 没有使用,被优化掉了。
然而并没有,让朋友把工程传过来看了一段时间才发现
在Vertex Shader 中的 out vec2 texCoord; 没有赋值!!
所以 texCoord 是被优化掉了的。
但是 在 FragmentShader 中的 in vec2 texCoord 是存在的变量,所以呢 glCompileShader 操作的时候是不会报错的!
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
难道 OpenGL 没有处理这个问题?当然不是。
这个错误是在 glLinkProgram 阶段抛出的。 朋友没有去获取 glLinkProgram 的状态,所以没有发现这个错误。

抛出了很详细的错误
- WARNING: Output of vertex shader ‘Color’ not read by fragment shader
- ERROR: Input of fragment shader ‘texCoord’ not written by vertex shader
- (lldb)
利用dSYM文件解析IOS的崩溃文件*.crash(开源小软件
看了很多介绍如何用命令行解析的贴子,由于本人太笨,敲命令行觉得太费劲,还有点记不住,写了这么一个小工具。开源的目的是可能有bug,希望大家帮忙完善。
使用非常简单,选中对应的crash、dsym、ipa文件拖入窗口中释放即可。
如果已经拖过对应的dsym和ipa会在本app文件夹内留下缓存、下次只拖入crash文件即可。占用空间过大时暂时请右键后选择『显示包内容』自行清理。
GitHub:https://github.com/wudijimao/VisualSymbolicateCrash
app下载:https://github.com/wudijimao/VisualSymbolicateCrash/raw/master/VisualSymbolicateCrash.app.zip
软件运行效果图如下:
PS:如果系统的符号解析不能请插入崩溃的手机打开XCode等待同步符号文件。
IOS开发中遇到转发delegate的时候不要再那么傻了
http://sam.dods.co/blog/2014/08/03/secondary-delegate/
delegate代理(截取delegate消息用)
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface MovieSDKDelegateProxy : NSObject <UITableViewDelegate> @property (nonatomic, weak)id primaryDelegate; @property (nonatomic, weak)id secondaryDelegate; @end
@implementation MovieSDKDelegateProxy
- (BOOL)respondsToSelector:(SEL)aSelector {
return ([self.primaryDelegate respondsToSelector:aSelector] || [self.secondaryDelegate respondsToSelector:aSelector]);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSObject *delegateForResonse = [self.primaryDelegate respondsToSelector:selector] ? self.primaryDelegate : self.secondaryDelegate;
return [delegateForResonse respondsToSelector:selector] ? [delegateForResonse methodSignatureForSelector:selector] : nil;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[self invokeInvocation:invocation onDelegate:self.primaryDelegate];
[self invokeInvocation:invocation onDelegate:self.secondaryDelegate];
}
- (void)invokeInvocation:(NSInvocation *)invocation onDelegate:(id)delegate
{
if ([delegate respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:delegate];
}
}
@end
use:注意设置每个delegate时必须重新将_proxy设置给tableView的delegate。这样才能保证tableView重新检查delegate支持的方法。(tableView会在设置delegate时将大多数方法都检查一下是否支持,回调时不再检查)
@implementation MovieSDKRefreshTableView {
MovieSDKDelegateProxy *_proxy;
}
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
self = [super initWithFrame:frame style:style];
if (self) {
_proxy = [MovieSDKDelegateProxy new];
_proxy.primaryDelegate = self;
[super setDelegate:self];
}
return self;
}
- (id<UITableViewDelegate>)delegate {
return _proxy.secondaryDelegate;
}
- (void)setDelegate:(id<UITableViewDelegate>)delegate {
_proxy.secondaryDelegate = delegate;
[super setDelegate:nil]; //Important:must reset delegate, because tableView check all response to @selecter when set delegate
[super setDelegate:_proxy];
}
@end
C++仿GCD多线程用法
typedef std::function<void(void)> MyFun;
typedef enum XTaskPriority {
XTaskPriority_High,
XTaskPriority_Default,
XTaskPriority_Low,
XTaskPriority_Background,
}XTaskPriority;
class XThreadPool;
class XTaskQueue {
std::mutex mutex;
std::queue<MyFun> queue;
public:
XThreadPool *runInPool;
static std::shared_ptr<XTaskQueue> getMainQueue() {
//TODO::maybe mutithread cause problem
static std::shared_ptr<XTaskQueue> mainQueue(new XTaskQueue());
return mainQueue;
}
static std::shared_ptr<XTaskQueue> getGlobleQueue(XTaskPriority priority) {
static std::shared_ptr<XTaskQueue> backgroundQueue(new XTaskQueue());
static std::shared_ptr<XTaskQueue> lowQueue(new XTaskQueue());
static std::shared_ptr<XTaskQueue> defaultQueue(new XTaskQueue());
static std::shared_ptr<XTaskQueue> highQueue(new XTaskQueue());
switch (priority) {
case XTaskPriority_Background:
return backgroundQueue;
case XTaskPriority_Low:
return lowQueue;
case XTaskPriority_High:
return highQueue;
case XTaskPriority_Default:
default:
return defaultQueue;
}
}
bool pop(MyFun &fun) {
std::unique_lock<std::mutex> lk(mutex);
if(queue.size() > 0) {
fun = std::move(queue.back());
queue.pop();
return true;
}
return false;
}
void push(const MyFun &fun) {
mutex.lock();
queue.push(fun);
mutex.unlock();
}
};
class XThreadPool {
private:
std::atomic_int_fast8_t threadNum;
int_fast8_t maxNum;
std::atomic_int taskNum;
std::mutex mutex;
std::condition_variable cv;
void runLoop() {
++threadNum;
do {
std::unique_lock<std::mutex> lk(mutex);
auto iter = taskQueue.begin();
auto end = taskQueue.end();
MyFun fun;
bool hasFun = false;
while (iter != end) {
if((*iter)->pop(fun)) {
hasFun = true;
break;
}
++iter;
}
if (hasFun) {
lk.unlock();
fun();
} else {
cv.wait(lk);
}
} while (1);
--threadNum;
}
public:
std::vector<std::shared_ptr<XTaskQueue>> taskQueue;
void onQueueChanged() {
mutex.lock();
cv.notify_one();
mutex.unlock();
}
//初始化两个全局Pool
static void initGlobelPool() {
static XThreadPool mainThread;
mainThread.taskQueue.push_back(XTaskQueue::getMainQueue());
XTaskQueue::getMainQueue()->runInPool = &mainThread;
mainThread.startNewThread();
static XThreadPool backgroundThreadsPool;
backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_High));
backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_Default));
backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_Low));
backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_Background));
XTaskQueue::getGlobleQueue(XTaskPriority_Background)->runInPool = &backgroundThreadsPool;
XTaskQueue::getGlobleQueue(XTaskPriority_Low)->runInPool = &backgroundThreadsPool;
XTaskQueue::getGlobleQueue(XTaskPriority_Default)->runInPool = &backgroundThreadsPool;
XTaskQueue::getGlobleQueue(XTaskPriority_High)->runInPool = &backgroundThreadsPool;
for (int i = 0; i < 4; ++i) {
backgroundThreadsPool.startNewThread();
}
}
void startNewThread() {
std::thread thread(std::bind(&XThreadPool::runLoop, this));
thread.detach();
}
XThreadPool() {
taskNum = 0;
}
virtual ~XThreadPool() {
}
};
struct XTask {
public:
std::function<void(void)> fun;
std::shared_ptr<XTaskQueue> queue;
std::chrono::time_point<std::chrono::system_clock> time;
bool operator <(const XTask& rh) {
return time < rh.time;
}
};
class XDispatchManager {
private:
std::mutex mutex;
std::condition_variable cv;
std::vector<XTask> mQueue;
void runLoop() {
do {
std::unique_lock<std::mutex> lk(mutex);
if (mQueue.empty()) {
cv.wait(lk);
} else {
cv.wait_until(lk, mQueue.begin()->time);
auto now = std::chrono::system_clock::now();
if (now >= mQueue.begin()->time) {
auto taskQueue = mQueue.begin()->queue;
taskQueue->push(mQueue.begin()->fun);
if (taskQueue->runInPool) {
taskQueue->runInPool->onQueueChanged();
}
mQueue.erase(mQueue.begin());
}
}
} while (1);
}
public:
static XDispatchManager* getSharedInstance() {
static XDispatchManager *manager = new XDispatchManager();
return manager;
}
XDispatchManager() {
XThreadPool *pool = new XThreadPool();
std::thread thread(std::bind(&XDispatchManager::runLoop, this));
thread.detach();
}
inline void dispatchAsnyc(std::shared_ptr<XTaskQueue> taskQueue, const MyFun &fun) {
taskQueue->push(fun);
if(taskQueue->runInPool) {
taskQueue->runInPool->onQueueChanged();
}
}
void dispatchAfter(std::shared_ptr<XTaskQueue> taskQueue, const MyFun &fun, long delayMS) {
XTask task;
task.fun = fun;
task.queue = taskQueue;
task.time = std::chrono::system_clock::now() + std::chrono::milliseconds(delayMS);
mutex.lock();
mQueue.insert(std::upper_bound(mQueue.begin(), mQueue.end(), task, [](const XTask &lh, XTask &rh)->bool {
return lh.time < rh.time;
}), task);
mutex.unlock();
cv.notify_one();
}
};
IOS第三方库YYKit作者一篇关于优化界面流畅度的文章
http://www.cocoachina.com/ios/20160208/15238.html?utm_source=tuicool&utm_medium=referral(强力推荐
http://www.cocoachina.com/ios/20160526/16457.html
http://www.cocoachina.com/ios/20150429/11712.html
C++11线程安全的队列
才知道一个新知识,还能这样提高效率:http://www.oschina.net/translate/a-fast-lock-free-queue-for-cpp?cmp(快速无锁队列)
typedef const std::function<void(void)> MyFun;
class XThreadPool {
private:
int mThreadNum = 0;
std::queue<MyFun> queue;
std::mutex mutex;
std::condition_variable cv;
void runLoop() {
do {
std::unique_lock<std::mutex> lk(mutex);
if(queue.size() > 0) {
queue.back()();
queue.pop();
} else {
cv.wait(lk);
}
} while (1);
}
public:
void startNewThread() {
std::thread thread(std::bind(&XThreadPool::runLoop, this));
thread.detach();
++mThreadNum;
}
XThreadPool() {
startNewThread();
}
XThreadPool(int threadNum) {
for (int i = 0; i < threadNum; ++i) {
startNewThread();
}
}
int getThreadNum() {
return mThreadNum;
}
virtual ~XThreadPool() {
}
void dispatchAsync(MyFun &fun) {
mutex.lock();
queue.push(fun);
mutex.unlock();
cv.notify_one();
}
void dispatchAfter(MyFun &fun, long delayMS) {
}
};
std::function详细用法示例
原文: http://www.jellythink.com/archives/771
不允许转载 先这样空着吧~
IOS KeyChain存储结构详解
先推荐一个链接:WHJ的一篇博文,最后一句话实际上很重要,我们下边也会提及。
苹果文档叫做:Keychain Services Reference
KeyChain可以用来储存加密的数据,或者相同TeamID应用间共享数据。并且卸载应用后不会被清除。
KeyChain的实现就相当于储存一个个的Dictionary(官方叫它SecItem),不过里边的key要按照系统规定方式设置。其中有几个重要的key(稍后会详细列出)。并且key有三个类型:标示整体类型的key(只有一个:kSecClass)、设置详细属性的key(kSecAttrGeneric、kSecAttrAccount、kSecAttrService、kSecAttrAccessGroup等)、最后一个是存储加密数据的key(kSecValueData等)
详细说这3个类型的Key:
1、标示类型的key只有一个kSecClass,并且必须设置。
[dic setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
Value值有5种、详细可以查看开头的链接或者苹果文档(Keychain Item Class Keys and Values节)。但通常我们只用示例里这一种。
这里的Value值不同会影响到SecItem里边存的加密内容以及可以使用的属性Key也有所不同。
2、详细属性key有很多个,并且可以使用的key根据kSecClass设置的值的不同会有不同。常用的就是:
(1)、kSecAttrGeneric(一般属性):
[dic setObject:@"this_is_a_test" forKey:(id)kSecAttrGeneric];
可以不设置,也可以设置,但不影响唯一性。
(2)、kSecAttrAccount(账号):
[dic setObject:@"mmmm" forKey:(id)kSecAttrAccount];
值是一个String,标志唯一的账号。(不设置则视为@””)。
(3)、kSecAttrService(服务):
[dic setObject:@"bbbb" forKey:(id)kSecAttrService];
值是一个String,标志唯一的服务。相当于与kSecAttrAccount一同看成一个联合主键,标志唯一的SecItem。存不同的内容应该使用不同的服务或账号。(不设置则视为@””)
(4)、kSecAttrAccessGroup(允许访问的Group):
[dic setObject:@"XXXXXXX.com.miao.TestApp" forKey:(id)kSecAttrAccessGroup];
如果不设置则自动使用plist里设置的第一个,通常plist里会配置成TeamID+BundleID,既是appID前缀加应用打包设置的BundleID。
关于其他的key,以及其中可以存的数据类型,可以参考苹果文档(Item Class Value Constants节),文档里有详细介绍,并且介绍了每一种kSecClass对应的可以使用的AttrKey。
3、储存数据的key,当SecClass为kSecClassGenericPassword时,我们使用以下代码:
[dic setObject:[NSData data] forKey:(id)kSecValueData];
接受NSData格式的数据,key不能随意改变。
当SecClass为其他值时,key可能会改变,对应接受的数据类型也会改变,详见苹果文档:Value Type Keys
当然你可以不储存数据也能正常保存一个SecItem。


